Saturday, December 26, 2009

Procedures

Using Procedures in Visual Basic


You might not be aware of it, but you've been working with subs and functions for a while. Event procedures such as Click() and Load() are subs, and Visual Basic comes with many predefined functions built right into it, such as LoadPicture() and Len().

Visual Basic is a procedural language--that is, you can make blocks of code that can be referred to by a name. After a block of code has a name, it can be called and executed. In other words, you can 
write some lines of code, enclose them in a code block, give the block a name, and then call the block when you need it. It's almost like having a program within a program. These little programs that live within larger programs are called "functions" if they return a value and "subs" if they don't.

Programmers have written user-defined subs and functions for years. (In fact, the term "sub" is a shortened form of "subroutine" that gradually became its own word.) They make coding easier, faster, and more robust. Also, making your own subs and functions puts you on the road to writing encapsulated and reusable code. Encapsulation is simply the methods and properties of an object enclosed behind a public interface.


BUILT-IN FUNCTIONS

String Functions
Function Name: UCase
Function Description: Returns the String that is passed in all uppercase letters.
Common Uses: UCase can be used when the desired output is required to be in all uppercase letters. It is also commonly used when you wish to validate data entered by a user against a given string.
Syntax: String = UCase(String)
Examples:

UCase(“Input String”)  returns “INPUT STRING”
UCase(“all lowercase”) returns  “ALL LOWERCASE”


Previous Way of Coding Validation:
If (txtVote.Text = “Bush” Or txtVote.Text = “BUSH” Or _ txtVote.Text = “bush” Then ... 
If (UCase(txtVote.Text) = “BUSH”) Then ...
Function Name: LCase
Function Description: Returns the String that is passed in all lowercase letters.
Common Uses: LCase is very similar in use to UCase.
Syntax: String = LCase(String)
Examples:

UCase(“Input String”) returns “input string”
UCase(“all lowercase”)  returns “all lowercase”


Function Name: Trim
Function Description: Returns a String with the same content, except the leading and trailing spaces are removed.
Common Uses: Often when data is gathered, additional spaces may exist before the first noncharacter or after the last nonblank character. 
It is good practice to remove these so that data may be presented cleanly.
Syntax: String = Trim(String)
Examples:

Trim(“ InputString”)  returns “InputString”
Trim(“InputString ”) returns “InputString”


The following code will initialize two Strings. 
One will contain a String that has the leading and trailing spaces removed by the Trim function. 
It is displayed between two vertical bars so that it will be obvious that the spaces have been removed. 
The second String will be created in a similar manner; however, the spaces will not be removed.
Dim strTest As String
Dim strWithBlanks As String
Dim strBorder As String
Dim strTrimmedOutput As String
Dim strUnTrimmedOutput As String

strTest = " Hello " 'Two spaces before and after
strBorder = "|"
strTrimmedOutput = strBorder & Trim(strTest) & strBorder
strUnTrimmedOutput = strBorder & strTest & strBorder
MsgBox(strTrimmedOutput)
MsgBox(strUnTrimmedOutput)
Function Name: Space
Function Description: Returns a String containing the number of spaces indicated by the parameter.


Common Uses: Often you wish to add spaces to set the total length of a String to an exact size. 
This is often used when working with fixed-width data files.
Syntax: String = Space(Integer)
Examples:

Space(5)  retruns “     “
Space(10) returns “         “


Function Name: Len
Function Description: Returns the number of characters contained in a String
Common Uses: Len is used to determine the size of a String.
Syntax: Integer = Len(String)
Examples:

Len(“Inconceivable”)  returns 3
Len(“Iocaine Powder”) returns 14


Function Name: Left
Function Description: Returns the first N characters of a String where N is an Integer parameter indicating the number of characters to return. 
If N is greater than the number of characters in the String, then the String is returned. 
No extra spaces are added.
Common Uses: Often you are only concerned with the first few characters of a String. Left is a great way to look at only the beginning of a String.
Syntax: String = Microsoft.VisualBasic.Left(String, Integer)
Examples:

Left(“Beginning of String”, 5)  returns “Begin”
Left(“Beginning of String”, 2) returns  “Be”

Function Name: Right
Function Description: Returns the last N characters of a String where N is an Integer parameter indicating the number of characters to return. 
If N is greater than the number of characters in the String, then the String is returned. 
No extra spaces are added.
Common Uses: Often you are only concerned with the last few characters of a String. Right is a great way to look at only the end of a String.
Syntax: String = Right(String, Integer)
Examples:

Right(“Ending of String”, 5)   returns “tring”
Right(“Ending of String”, 2)  returns “ng”


Function Name: Mid
Function Description: Returns a specific number of characters of a String allowing the developer to indicate where to start and how many characters to return. 
The first parameter is the source String. 
The second is an Integer indicating the starting position to copy from. 
The third parameter is optional and indicates the number of characters to copy. 
If the third parameter is left out, all characters from the starting position are returned.
Common Uses: Often you wish to extract a portion of a String to use separately from the rest of the String. This is often the case when working with fixed-width data files.
Syntax: String = Mid(String, Starting Position, Optional Length)
Examples:
 
Mid(“This is the String”, 6, 2)   returns “is”
Mid(“This is the String”, 9, 3)  returns “the”


Function Name: InStr
Function Description: Returns the position of the first occurrence of a substring that is searched for in the String passed.


InStr(“ab ab ab”, “a”)  returns 1
InStr(“ab ab ab”, “c”)  returns 0



Common Uses: InStr can be used to tell us if a String has a certain substring contained within it. It operates much like searching a document for a word.

Syntax: Long = InStr(String to be Searched, Search String)


Examples:
 
InStr(“This is a very”, “is”)  retruns 3
InStr(“ab ab ab”, “ab”) returns 1

Conversion Functions


Function Name: Str
Function Description: Returns a String representation of the numeric value passed to it. 
By default it will place a single space in front of the first numeric character.
Syntax: String = Str(Numeric Value)
Examples:
'Proper conversion
Dim strDestination As String
Dim intSource As Integer
intSource = 1
strDestination = Str(intSource)
Function Name: Val
Function Description: Returns a numeric representation of the String value passed to it. 
Val will convert a String to a numeric until it reaches a character that is not a numeric value, a decimal point, or a white-space character. 
Once an unrecognizable character is read, conversion stops at that point.
Common Uses: Val is used much in the same manner as Str, except in the opposite direction.
Syntax: Numeric Value = Val(String)
Examples:

Val(“199.11”) retruns 199.11
Val(“ 199.11 “) retruns 199.11


Function Name: CDate

Function Description: Returns a Date representation of the String value passed to it. 
Common Uses: CDate is used when a Date representation is needed. 
Often a date can be stored in a String when it is gathered from a fixed-width or comma-delimited file. 
If proper operations are going to be performed on the date, then it is necessary to store it in its native format.
Syntax: Date = CDate(String)
Examples:
Dim dteToday As Date
Dim strToday As String
Dim strTomorrow As String
Dim dteTomorrow As Date
strToday = "September 30, 2001"
dteToday = CDate(strToday)
dteTomorrow = DateAdd(DateInterval.Day, 1, dteToday)
strTomorrow = CStr(dteTomorrow)
MsgBox(strTomorrow)

Mathematical Functions


Function Name: Int, Fix
Function Description: Returns the Integer portion of the numerical value passed to it.
Common Uses: Int or Fix are used when you wish to convert a numerical value to an integer without regard for the decimal value. It performs a truncation of the number.
Syntax: Integer = Int(Numerical Value)
  Integer = Fix(Numerical Value)

For e.g. Int(199.11) returns 199.

Miscellaneous Functions
Function Name: IsNumeric
Function Description: Returns True if the value passed to it evaluates to a numeric data type, otherwise it returns False.

Common Uses: IsNumeric can be used to verify that a value can be evaluated as a number. Instead of possibly getting either inaccurate results or a run-time error, by using IsNumeric, a proactive approach to error handling can be achieved.
Syntax: Boolean = IsNumeric(Expression)

For e.g. IsNumeric(“ABC”)

Function Name: IsDate
Function Description: Returns True if the value passed to it evaluates to a valid Date, otherwise it returns False.
Common Uses: IsDate can be used when you are about to convert a value to a Date representation. 
Instead of possibly getting either inaccurate results or a run-time error, by using IsDate, a proactive approach to error handling can be achieved.
Syntax: Boolean = IsDate(Expression)

for e.g. IsDate(“January 1, 2001”) 


Function Name: Today

Function Description: Returns the current system date.
Common Uses: Today can be used anytime the developer wishes to access the system date. 
While it is returned as a variant, it can be used as a native date format or converted to a String representation.
Syntax: Date = Today()
Examples:
'Code to display Yesterday’s Date in a MessageBox
Dim dteToday As Date
Dim dteYesterday As Date
Dim strYesterday As String

dteToday = Today()
dteYesterday = DateAdd(DateInterval.Day, -1, dteToday)
strYesterday = CStr(dteYesterday)
MsgBox(strYesterday)
Function Name: TimeOfDay
Function Description: Returns the current system time.
Common Uses: TimeOfDay can be used anytime the developer wishes to access the system time. 
While it is returned as a variant, it can be used as a native date format or converted to a String representation. 
You can store a Time in the DateTime data type.
Syntax: DateTime = TimeOfDay()
Examples:
'Code to display the time now in a MessageBox
Dim dteExactTime As DateTime
dteExactTime = TimeOfDay()
MsgBox(dteExactTime.ToString())
Function Name: Rnd
Function Description: Returns a pseudo random decimal number that is greater than or equal to 0 and less than 1. 
By passing Rnd an optional numeric parameter, you can further control the type of random number you generate.
Common Uses: Random numbers are required for a great many reasons. 
By combining the output of the Rnd function with some basic mathematics, random numbers can be generated within a given range.
Syntax: Variant = Rnd()
Examples:
Function Name: Rnd
Function Description: Returns a pseudo random decimal number that is greater than or equal to 0 and less than 1. 
By passing Rnd an optional numeric parameter, you can further control the type of random number you generate.
Common Uses: Random numbers are required for a great many reasons. 
By combining the output of the Rnd function with some basic mathematics, random numbers can be generated within a given range.
Syntax: Variant = Rnd()
Examples:

Function Name: Format
Function Description: Returns a String representation of the expression passed formatted according to instructions passed to it in the second parameter. Format may be used to improve the appearance of numbers, dates, times, or string values.
Common Uses: Format is used anytime the user needs to beautify the output of a value.Syntax: String = Format(Expression, String)

USER DEFINED FUNCTIONS / SUBROUTINES

Making code changes easily

Subs enable you to change code easily. If you have a body of code that you need to use repeatedly, put the code in a sub. Then, if you need to make a change in the code, simply go to the sub to make changes. If you don't put the code in a sub, you will have to go to every instance of the code in your program to make the required change. The more dispersed your code is, the harder it is to
make changes effectively and efficiently.


A sub is a procedure that executes the lines of code within its block but doesn't return a value. The syntax for a simple sub is as follows:

[Private|Public] Sub SubName()
.....lines of code
End Sub

In this syntax

[Private|Public] are the optional Visual Basic keywords that define the scope of the sub.

Sub is the Visual Basic keyword that denotes the type of procedure.

SubName is the name that you assign to your sub.

End Sub are the Visual Basic keywords that denote the end of a code block.

The following code snippet is an example of a simple sub:

Public Sub DataNotFound()
MsgBox "Data Not Found", vbInformation
End Sub

When you call this sub from other areas of your code, the sub displays a Windows message box with the string Data Not Found. The message box will display whatever text is used for the string constant.

Listing 3.1 shows the sub being called with the Visual Basic Call statement (line 2). Using the Call statement is optional. Although you can call a Sub by using only its name, using the Call keyword makes
your code more readable.


LISTING 3.1 18LIST01.TXT--Calling a Sub from an Event Procedure

01 Private Sub itmOpen_Click()
02 Call DataNotFound
03 End Sub


Making Subs by Using Add Procedure


You can add a sub to your project in two ways:

By writing the code directly into the General Declarations section of a form or module.

By using the Tools menu's Add Procedure option.


Enabling the Add Procedure menu item

For the Add Procedure menu item to be enabled, you must be in Code window view of the form or module into which you want to add the procedure.



Add a sub to your project with Add Procedure
1. From the Tools menu, choose Add Procedure to display the Add Procedure dialog.

2. Enter the sub Name
3. Click OK to add the sub's code block to the form or module (see Figure 18.2).


.

After you create the sub code block with the Add Procedure dialog, you add the procedure's code within the code block. Don't enter any code for the Sub after the End Sub keywords; this is illegal and
generates syntax errors when you compile the code.

Defining function

Making a Simple Function


A function is a procedure that executes lines of code and returns a value. The syntax for declaring a simple function is as follows:

[Private|Public] Function FunctionName() As DataType
...lines of code
FunctionName = ReturnValue
End Function

In this syntax

Private|Public are the optional Visual Basic keywords that define the scope of the function.

Function is the Visual Basic keyword that denotes the procedure is a function.

FunctionName is the name that you assign to your function.

As is the Visual Basic keyword that denotes a data type assignment.

DataType is the data type of the value that the function will return.

ReturnValue is the value that you pass back from the function by assigning it to the function's name. (This is very important!)

End Function are the Visual Basic keywords that denote the end of a code block.

The code snippet in Listing 3.2 shows a function, GetNumber(), the purpose of which is to return a number defined within the function itself.


LISTING 3.2 18LIST02.TXT--A Simple Function

01 Public Function GetNumber() As Integer
02 Dim a%
03 Dim b%
04 Dim c%
05 `Assign values to some variables
06 a% = 7
07 b% = 12
08
09 `Add them together
10 c% = a% + b%
11
12 `Pass the result out of the function by assigning
13 `it to the function name.
14 GetNumber = c%
15 End Function

You add a function to your project by using the same two methods that you use to add a sub--by putting it directly into the General Declarations section of the form or module or by using the Add
Procedure dialog. However, be advised that you have to manually add a little code when you add a function to your code by using the Add Procedure dialog

Passing Arguments into Subs and Functions


You can enhance the power and versatility of subs and functions by using arguments. An argument, also referred to as a parameter, is a variable that acts as a placeholder for a value that you'll pass into
the sub or function. You create arguments by placing them within the parentheses of the declaration statement of the sub or function. The following snippet of code shows the declaration for the function
EndDay(), which takes two arguments: one of type Integer and one of type String.

EndDay(NumOne As Integer, strName As String) As Integer

Using arguments greatly increases the reusability of your code. For example, imagine that in many places of your code you need to figure out the greater of two numbers. Every time you need to do this
calculation, you could write out the code, line for line, or you could write out a function that does this for you and then call the function when you need to do the calculation. The advantage of the latter
method is twofold:

One call satisfies many needs throughout your code.

If you need to enhance this functionality, you don't have to go through your code and make enhancements line by line; you simply go back to the function and make the changes within the function's code block.

Listing 3.3 shows the user-defined function GetGreaterNum(), which returns the greater of two numbers passed to it.


LISTING 3.3 18LIST03.TXT--A Simple Function That Takes Arguments

01 Public Function GetGreaterNum(NumOne As Integer, _
NumTwo As Integer) As Integer
02 `If the first number is greater than the second
03 If NumOne > NumTwo Then
04 `return the first number
05 GetGreaterNum = NumOne
06 Else
07 `if not, return the second number
08 GetGreaterNum = NumTwo
09 End If
10 End Function

Listing 3.4 shows the GetGreaterNum() function called from within a Click() event procedure.





LISTING 3.4 18LIST04.TXT--Using a Function Within an Event Procedure

0 1 Private Sub cmdGreaterNum_Click()
02 Dim i%
03 Dim j%
04 Dim RetVal%
05
06 `Get the input in txtNumOne and convert it to an integer
07 i% = CInt(txtNumOne.Text)
08
09 `Get the input in txtNumTwo and convert it to an integer
10 j% = CInt(txtNumTwo.Text)
11
12 RetVal% = GetGreaterNum(i%, j%)
13
14 `Take the result from the function, convert it to a
15 `string and assign it to the caption of the button.
16 cmdGreaterNum.Caption = CStr(RetVal%)
17 End Sub

It's very important when you use subs or functions that the argument's type and order match up. If you have a procedure that has three arguments of type Integer, you must pass in three integers; if you pass
in two Integers and a String, the compiler will throw an error. For example, if you have a function EndDay() declared as follows,

Public Function EndDay(iNum As Integer, dAccount _
As Double) As Double

and call the function by using the following line of code,

dMyResult = EndDay(6, "D56R")

this call generates an error. "D56R" is of type String, but the function is expecting the second argument to be of type Double. For this reason, a variable type declaration error appears.

Also, the argument count must match up. For example, you have a function declared as follows:

Public Function Bar(iNum as Integer, dNum as Double, _
strName as String) as Integer

and you call the function by using the following line of code:

iMyResult = Bar(6, 7)

This call also causes an error. The function expects three arguments, but you've passed in only two. Again, an error occurs.

It's possible to make an argument optional by using the Optional keyword before an argument when you declare the function. If there's an upper limit on the number of arguments that you're going to pass,
you should use the Optional keyword. These arguments must be declared as Variant.

Named Arguments

Using Named Arguments, Avoid problems with named arguments

Using named arguments enables you to avoid problems that might arise in function use because of the ordering of arguments.


You can use named arguments to make passing arguments to a procedure easier. A named argument is the literal name of an argument in a procedure. For example, if you have a function EndDay() that
takes two arguments, NumOne and NumTwo, of type Integer, you define it as follows:

EndDay(NumOne as Integer, NumTwo as Integer) as Integer

To pass a value to the function by using named arguments, you use the names of the arguments and assign values to them by using the := characters. Thus, to pass actual values into EndDay() by using
named arguments, you do the following:

X = EndDay(NumOne:=3, NumTwo:=4)

Exiting Subs and Functions


Sometimes you need to leave a procedure before it finishes. You do this by using the Exit keyword. Listing 3.5 shows the function ExitEarly(), which takes two arguments: an Integer used to determine the
upper limit of a loop and an Integer that flags the function when a special condition exists that requires the function to be exited early.


LISTING 3.5 18LIST05.TXT--Using the ExitEarly() Function

01 Public Function ExitEarly(iLimit As Integer, _
iFlag As Integer) As Integer
02 Dim i%
03 Dim Limit%
04 Dim Flag%
05
06 `Assign the limit argument to a local variable
07 Limit% = iLimit
08
09 `Assign the state argument to local variable
10 Flag%= iFlag
11
12 `Run a For...Next loop to Limit%
13 For i% = 0 To Limit%
14
15 `If the passed in state is one
16 If Flag% = 1 Then
17
18 `Check to see if i% equals half the value of
19 `the Limit variable
20 If i% = Limit% / 2 Then
21
22 `If it does, pass out the value of i%
23 `at that point
24 ExitEarly = i%
25
26 `Terminate the function; there is no
27 `reason to go on
28 Exit Function
29 End If
30 End If
31 Next i%
32
33 `If you made it this far, the flag variable does not
34 `equal one, so pass the value of i% out of the
35 `function by assigning the value of i% to the
36 `function name.
37 ExitEarly = i%
38
39 End Function

The ExitEarly() function works by taking the iLimit argument, assigning it to a variable local to the function (line 7) and then using that local variable to be the upper limit of a For...Next loop (line 13). The
function also takes the iFlag argument and assigns that variable to one that's also local to the function (line 10). Then the For...Next loop is run. Within the loop, if the value of the local Flag variable is 1 (line
16), an If...Then statement checks the value of the counting variable, i%, to see whether it's equal to half the value of the variable Limit (line 20). If it is, the value is assigned to the function's name (line 24) to
be passed back to the call, and the Exit keyword terminates the execution of the function (line 28). If the value of the local variable Flag is other than 1, the loop continues until it reaches its limit (line 31).
Then the value of i% is assigned to the function's name, and control is returned to the calling code (line 36).

When you create a user-defined function, the Visual Basic IDE treats it as though it were an intrinsic function. This means that the function is listed in the Object Browser and appears in the Quick Info window

Scope

Understanding Scope


Scope is the capability of two different variables to have the same name and maintain different values and lifetimes. Listing 3.6 shows two functions, EndDay() and Bar().


LISTING 3.6 LIST05.TXT--Two Variables with Different Scope

01 Public Function EndDay() as Integer
02 Dim x as Integer
03 Dim y as Integer
04
05 x = 2
06 y = 7
07 EndDay = x + y
08 End Function
09
10 Public Function Bar() as Integer
11 Dim x as Integer
12 Dim y as Integer
13
14 x = 12
15 y = 34
16 Bar = x * y
17 End Function

Notice that each function declares variables x and y. Also notice that those variables are assigned different values within each function (lines 5-6 and 14-15). This is possible because each set of variables exists only where it's created. In the function EndDay(), the variables x and y are created in lines 2 and 3. When the function ends in line 8, the variables are removed from memory and no longer exist. (This is known as going out of scope.) The same is true of the x and y variables in the Bar() function. Neither variable set can see the other. If you wanted to create variables i and j, the values of which could be seen by both functions, you would create the variables higher in scope in the General Declarations section of a form or module, using the Public or Private keywords when declaring them.


Documenting Subs and Functions

Although the EarlyExit()function is functionally adequate, it's difficult to implement from project to project. If other programmers wanted to use it, they would have to take more than a passing glance to figure out what the function is about and how to put it to good use. Proper documentation addresses this deficiency.

All subs and functions should have a header, a section of commented code that appears at the top of a code block. The header usually gives a synopsis of the procedure: the procedure name, a description of the arguments and return value if any, and some remarks as to what the procedure is about, with any special instructions. Also in the header is a history of when and who created the code. If any changes are made to the code, a description and date of the changes are added to the header. Finally, the header contains the appropriate copyright information.

You should also comment each task within the procedure. This saves time for others who will maintain your code. Commenting your code will save you a lot of effort when it comes time to revisit the code later. Listing 3.7 shows the ExitEarly() function commented in a professional manner (line numbering has been omitted for the sake of clarity).




LISTING 18.7 18LIST06.TXT--A Well-Documented Function

01 Public Function ExitEarly(iLimit As Integer, _
02 iFlag As Integer) As Integer
03 `****************************************
04 `Sub/Function: ExitEarly
05 `
06 `Arguments: iLimit The upper limit of the For..Next Loop
07 ` iFlag An integer indicating early exit from
08 ` the function. 1 = Exit.
09 ` Other values are ignored.
10 `
11 `Return: The value of the For...Next loop counter
12 `
13 `Remarks: This function is used to demonstrate the way
14 ` to use arguments within a function
15 `
16 `Programmer: Bob Reselman
17 `
18 `History: Created 4/20/98
19 `
20 `Copyright 1998, Macmillan Publishing
21 `****************************************
22
23 Dim i% `Counter variable
24 Dim Limit% `Internal variable for the upper limit of the
25 `For...Next loop
26 Dim Flag% `Internal variable for the exit flag
27
28 `Assign the limit argument to a local variable
29 Limit% = iLimit
30
31 `Assign the state argument to local variable
32 Flag% = iFlag
33
34 `Run a For...Next loop to Limit%
35 For i% = 0 To Limit%
36
37 `If the passed in state is one
38 If Flag% = 1 Then
39
40 `Check to see if i% equals half the value of
41 `the Limit variable
42 If i% = Limit% / 2 Then
43
44 `If it does, pass out the value of i%
45 `at that point
46 ExitEarly = i%
47
48 `Terminate the function; there is no reason
49 `to go on
50 Exit Function
51 End If
52 End If
53 Next i%
54
55 `If you made it this far, the state variable does not
56 `equal one, so pass the value of i% out of the function
57 `by assigning the value of i% to the function name.
58 ExitEarly = i%
59
60 End Function

Determine entry with Sub Main()

Determining Your Entry Point with Sub Main()


By default, when you start a Visual Basic project, the first form created will be the first form that the project loads. This is adequate if you have a one-form project. What if you have a project with many
forms or a project with no forms at all? For the multiple-form problem, you could "chain" Form_Load() event procedures--have one form's Load event procedure Load another form--as follows:

Private Sub Form_Load()
Load frmAnotherForm
End Sub

This would work adequately for projects with a limited amount of forms, but it's not the best programming practice, particularly if you have projects that require the presentation of many forms.

For projects that have no forms (and there are such projects, particularly in server-side Internet programs that use Visual Basic), there's nothing to load. What do you do for an entry point (or starting
point) for your program? Visual Basic provides a nonform-based entry point for your program--the Sub Main() procedure. Sub Main() is a special procedure reserved by Visual Basic as the startup
procedure for any given project. Sub Main() must be declared in a module, and there can be only one Sub Main() per project.

Set Sub Main() to be the startup point for a project
1. From the Project menu, choose ProjectName Properties.

2. Select Sub Main from the Startup Object drop-down list on the General page of the Project Properties dialog (see Figure 18.5).

3. Click OK.


FIGURE 18.5 You can choose Sub Main() or any form in your project as the startup object.

After you define Sub Main() to be the startup object for your project, you need to create Sub Main in a module. You can use the Add Procedure dialog that you've used to create user-defined procedures,
or you can manually enter the declaration in the General section of your chosen module. Remember, a project can have only one Sub Main(). After you create Sub Main(), you need to fill in some startup code.

Listing 3.8 shows a Sub Main() that displays two forms by using the Show method (lines 4 and 5) and then displays a message box after all the forms are visible (line 8).


LISTING 3.8 18LIST07.TXT--A Simple Sub Main()

01 Sub Main()
02 `Use the Show method to display both
03 `forms upon startup
04 frmMain.Show
05 frmOther.Show
06
07 `Report that all forms are shown
08 MsgBox "Everything shown"
09 End Sub

The code for some versions of Sub Main() can be simple, but some Sub Main() procedures can be complex. Listing 3.9 shows how Sub Main() is used to invoke a comprehensive startup routine that calls other procedures.

LISTING 3.9 18LIST09.TXT--A More Complex Sub Main()

01 Sub Main()
02 `Load the form and let it run the code in the
03 `Form_Load event handler
04 Load frmMain
05 `Intialize the contact list combo box
06 Call InitComboAsDb(frmMain.cboName, frmMain.DataMain)
07 `Fill the appointment list with today's appoinments
08 Call GetDailyAppointments(CDbl(Cdate _
(frmMain.FormDateString())), _
frmMain.lstSchedule, _
frmMain.DataMain, _
gAppointmentDelta%)
09 `Show the main form
10 frmMain.Show
11 `Set the mouse pointer back to an arrow
12 frmMain.MousePointer = 0
13 End Sub

ENUM

ENUM

Syntax
[ Private | Public ] Enum EnumName
name1 [ = value1 ]
name2 [ = value2 ]
.
.
.
nameN [ = valueN ]
End Enum
Usage
The Enum statement defines a list of constants. Private means that the constants will be local to the module in which they are declared. Public means that you can reference the constants from anywhere in the program. If neither Public nor Private is specified, then Public will be assumed.
Arguments
EnumName--a valid Visual Basic identifier that describes the collection of constants.
name1--a valid Visual Basic identifier that is associated with a particular constant.
value1--a Long value that can be any expression including using other constants and enum values. If this value is omitted, it will default to zero.
name2--a valid Visual Basic identifier that is associated with a particular constant.
value2--a Long value that can be any expression including using other constants and enum values. If this value is omitted, it will default to one or one more than the previous constant.
nameN--a valid Visual Basic identifier that is associated with a particular constant.
valueN--a Long value that can be any expression including using other constants and enum values. If this value is omitted, it will default to one more than the previous constant.
Examples
Public Enum MyBits
bit1 = 2
bit2 = 4
Bit3 = 8
bit4 = 16
Bit5 = 32
Bit6 = 64
Bit7 = 128
End Enum
Private Sub Command1_Click()
MsgBox "The value of bit 1 in B is " & Format(Asc("B") Mod bit1)
End Sub
This program uses enumerated set of values MyBits to examine the lowest order bit of the character B. Since the ASCII value of B is 01000010 the answer will be zero.
See Also:
Const (statement), Dim (statement)


WITH STATEMENT
Syntax
With object
[ list of statements ]
End With
Usage
The With statement is used to qualify a single object or user-defined type. Properties of the qualified object may omit the object name and begin with a period (".").
Arguments
object--the name of the object to be qualified.
Examples
Private Sub Command1_Click()
With Text1
.Text = "Testing the with statement."
.Font.Size = 36
.Font.Bold = True
End With
End Sub
This routine uses a With statement to simplify assigning values to Text1 text box object.
See Also:
Object (data type)

Arrays

What Is an Array?


A collection of similar variables, in which each has the same name and all are of the same type, is an array. Remember that a variable can be thought of as a cup that holds an unknown value or an always changing value

Think of an array, then, as a collection of cups. Each cup in the collection can hold the same type of data, and every cup in the collection has the same name. Each cup within the collection is an element and has a number assigned to it that reflects its position within the collection. The first element of an array usually has a position number of 0 (zero).

Arrays can be different sizes. One array might have three elements, another might have 30 elements, and it's even possible for an array to have no elements at all--only the possibility of having elements that are created at a later time.

Declaring Arrays

You can declare, or dimension, an array in two ways: as you would a single variable and by using the To keyword.

Declaring an Array like Declaring a Single Variable
To declare an array as you would a single variable, you use the following syntax:

Dim|Public|Private ArrayName(Subscript) As DataType

In this syntax, Dim, Public, and Private are Visual Basic keywords that declare the array and its scope. If you use Dim, the array is private to the procedure in which it is declared. Public makes the array visible from anywhere in the program, and Private (within the General section of a form or module) makes the array visible only to the form or module in which it's declared. If you use Dim within a module's procedure, the array will be available to only that procedure, but if you use Dim in the module's Declarations section, the array will be available to all procedures within the module.

ArrayName is the name of the array.




Option base

When you declare an array, the first element of the array is usually 0 (zero). It's possible, however, to force the first element of an array to be 1. To do this, insert the statement Option Base 1 in the General section of a module within your project. You have to do this only when you want the first element of your array to be element number 1.

Subscript is the number of the highest element in the array. Remember that the first element in an array is usually zero, so if you declare an array in which Subscript is 6, you'll have seven element positions and, therefore, seven elements.

As is the Visual Basic keyword that signifies a type declaration.

DataType is any valid Visual Basic data type, such as Integer or Double.

Therefore, to declare an array of integers with five elements in it, you would use the following:

Dim iMyArray(4) As Integer

To assign a value to each element in the array iMyArray, you would use the following:

iMyArray(0) = 9
iMyArray(1) = 342
iMyArray(2) = 2746
iMyArray(3) = 0
iMyArray(4) = 8901

To change the value of the fourth element of the array iMyArray from 0 (zero) to 45, you would do as follows:

iMyArray(3) = 45

To declare an array of nine strings global to your entire program, you would use the code in Listing 3.10.


LISTING 3.10 LIST01.TXT--Assigning Values to an Array's Elements

01 Public strMyArray(8) As String
02
03 strMyArray(0) = "I am a pitcher."
04 strMyArray(1) = "I am a catcher."
05 strMyArray(2) = "I play first base."
06 strMyArray(3) = "I play second base."
07 strMyArray(4) = "I play third base."
08 strMyArray(5) = "I play shortstop."
09 strMyArray(6) = "I play left field."
10 strMyArray(7) = "I play center field."
11 strMyArray(8) = "I play right field."


Declaring an Array with the To Keyword


You can also declare an array by using the To keyword within the subscript syntax. For example, if you want to create an array of five Integer variables in which the first element is number 1 and the last element is number 5, you use

Dim iMyArray(1 To 5) as Integer

This method provides an easy way to start your element numbering at a value other than 0 (zero).


Dynamic arrays

Although you usually set the number of elements in an array when you declare it, it's possible to alter the size of the array. When you change the number of elements in an existing array, you redimension it.
To do so, use the ReDim keyword in the following syntax:

ReDim [Preserve] ArrayName(Subscript) As DataType

In this syntax,

ReDim is the Visual Basic keyword denoting that the array is being redimensioned.

Preserve is an optional Visual Basic keyword that forces all pre-existing elements in the array to hold their values. If you don't use the Preserve keyword when you redimension the array, the value of all elements will be changed to zero for numeric data types, and a zero-length string ("") for variable-length strings. Fixed-length strings will be filled with zeros, and variants will be initialized to EMPTY, which could be either zero or a zero-length string, depending on the expression.

ArrayName is the name of the array.

Subscript is the subscript for the highest element in the array.

As is the Visual Basic keyword that signifies a type declaration. When redimensioning an array, the As keyword is optional.

DataType is any valid Visual Basic data type, such as Integer or Double. When redimensioning an array, the DataType is optional and can't be changed with the Redim keyword unless the array is of type Variant.

Using ReDim in your code

The actual implementation of the ReDim statement is different from this conceptual illustration. If you create an array that you'll later redimension, you can't hard code the element size of the array when you first declare it. Therefore, in practice, the code here won't re dimension the array declared in Listing 3.10


The following code shows how, conceptually, to redimension the array strMyArray (from Listing 3.10) to contain 10 elements:

ReDim Preserve strMyArray(9)
strMyArray(9) = "I am the designated hitter."

To create an array that you'll later resize, you must first create the array without any elements. Listing 3.11 shows the proper way to create an array that you'll later redimension.


LISTING 3.11 LIST02.TXT--Diming an Array Without Any Elements Before
ReDiming It

01 `Create an array without any elements
02 Dim strMyArray() as String
03
04 `Dimension the array for 9 elements
05 ReDim strMyArray(8)
06
07 `Assign values to the array elements
08 strMyArray(0) = "I am a pitcher."
09 strMyArray(1) = "I am a catcher."
10 strMyArray(2) = "I play first base."
11 strMyArray(3) = "I play second base."
12 strMyArray(4) = "I play third base."
13 strMyArray(5) = "I play shortstop."
14 strMyArray(6) = "I play left field."
15 strMyArray(7) = "I play center field."
16 strMyArray(8) = "I play right field."
17
18 `Add an element and make it so all the values
19 `of the previous elements are kept intact
20 ReDim Preserve strMyArray(9)
21
22 `Assign a value to the new array element
23 strMyArray(9) = "I am the designated hitter."

Notice in Listing 3.11 that the first ReDim statement (line 5) doesn't use the Preserve keyword. You can do this because initially there are no values within the array to preserve. In the second ReDim statement on line 20, however, the Preserve keyword is very important because the pre-existing elements have assigned values that you don't want to lose. If you didn't use the Preserve keyword in the second ReDim statement, strMyArray would have the following values:

strMyArray (0) = ""
strMyArray (1) = ""
strMyArray (2) = ""
strMyArray (3) = ""
strMyArray (4) = ""
strMyArray (5) = ""
strMyArray (6) = ""
strMyArray (7) = ""
strMyArray (8) = ""
strMyArray (9) = "I am the designated hitter."

Remember, arrays are tricky. Be careful!

Multidimensional Arrays

So far, the arrays in this chapter have been one-dimensional arrays--that is, they are a one-row collection of variables, however, you can create arrays that have up
to 60 dimensions. Usually, two-dimensional arrays will suffice for most introductory programming projects, and most likely you won't need to make an array of more than three dimensions.

Think of a two-dimensional array as a tic-tac-toe board--a set of columns and rows that intersect to form a grid. Each grid cell has a location defined as ColumnNumber, RowNumber. Each element is defined by the coordinates of the column position and the row position. For example, the array element iVar(0, 0) is 5 and the element iVar(2,2) is 49.
To create a two-dimensional array, use the following syntax:

Dim|Public|Private ArrayName(SubscriptOfCols, _
SubscriptOfRows) As DataType

In this syntax,

Dim, Public, and Private are Visual Basic keywords that declare the array and its scope. If you use Dim, the array is private to the procedure in which it's declared. Public makes the array visible from
anywhere in the program, and Private (within a form or module's General section) makes the array visible only to the form or module in which it's declared. Using Dim within a module automatically
makes the array available anywhere in the program, as if the Public keyword were used.

ArrayName is the name of the array.

SubscriptOfCols is the number of the highest column in the array.

SubscriptOfRows is the number of the highest row in the array.

As is the Visual Basic keyword that denotes type declaration.

DataType is any valid Visual Basic data type.

Therefore, to declare the array you would use the following syntax:

Dim iVar(2,4) as Integer

Whereas you can consider a two-dimensional array to be a rectangle, you can consider a three-dimensional array to be a rectangular block. To declare a three-dimensional array of integers, use

Dim iVar(1,2,1) as Integer


In the array iVar, the value assigned to each element, as depicted in Figure 11.4, is as follows:

iVar(0,0,0) = 5
iVar(0,1,0) = 187
iVar(0,2,0) = 16
iVar(1,0,0) = 12
iVar(1,1,0) = 55
iVar(1,2,0) = 7
iVar(0,0,1) = 34
iVar(0,1,1) = 13
iVar(0,2,1) = 4500
iVar(1,0,1) = 612
iVar(1,1,1) = 9
iVar(1,2,1) = 784

Just as with a one-dimensional array, you can use the To keyword when declaring the subscript range of each dimension in a multidimensional array. Thus, the line

Dim dMyArray(1 To 5, 3 To 8, 3 To 5) As Double

would declare a three-dimensional array of Double values, five columns wide by six rows tall by three planes deep.

You can also use the Redim keyword to resize a multidimensional array. If you use the Preserve keyword, however, only the last dimension of a multidimensional array can be resized, and the number of dimensions can't be changed.

Using Loops to Traverse an Array

You can use the For...Next loop that you learned about in Chapter 10, "Working with Loops," to move through, or traverse, an array. This can be useful when you want to change or report values in an array.

Listing 3.12 is an example of using a For...Next loop to traverse an array. The listing is the event procedure for a button click, in which an array of 20 elements is created. A For...Next loop is used twice--first to assign values to every element in the array, and then to find the value assigned to every element and make a string that reports the value of that element

LISTING 3.12 LIST03.TXT--Using For...Next Loops to Traverse an Array

01 Private Sub cmdTraverse_Click()
02 Dim i%
03 Dim iMyArray%(19) As Integer
04 Dim BeginMsg$
05 Dim MidMsg$
06 Dim LoopMsg$
07 Dim FullMsg$
08
09 `Assign a value to each element in the array
10 `by using a loop to traverse to each element
11 `in the array.
12 For i% = 0 To 19
13 `Make the value of the element to be
14 `twice the value of i%
15 iMyArray%(i%) = i% * 2
16 Next i%
17
18 `Create the BeginMsg$ string
19 BeginMsg$ = "The element is: "
20 MidMsg$ = ", The value is: "
21 `Go through the array again and make
22 `a string to display
23 For i% = 0 To 19
24 LoopMsg$ = LoopMsg$ & BeginMsg$ & CStr(i%)
25 LoopMsg$ = LoopMsg$ & MidMsg$ & iMyArray(i%)
26
27 `Concatenate the loop message to the
28 `full message. Also add a line break
29 FullMsg$ = FullMsg$ & LoopMsg$ & vbCrLf
30
31 `Clean out the loop message so that
32 `new value next time through the loop
33 LoopMsg$ = ""
34 Next i%
35
36 txtTraverse.Text = FullMsg$
37 End Sub

The code in Listing 3.12 is simple. Remember that a For...Next loop increments a counting variable as it loops. You assign this counting variable to the element position of the array. Then, set the lower bound of the array (the lowest element number) to the start point of the loop and set the upper bound of the array (the highest element number) to the end point of the loop. When you run the loop, the counting variable will always be at an element of the array.

List Box

LISTBOXES

Of all the standard controls, the ListBox and ComboBox are best suited for categorizing and listing information from which users can make choices and selections. These controls are closely related, and the manner in which you manipulate them is almost identical. The difference is that the ComboBox combines a ListBox control with a TextBox (hence the name) and allows users to select from a list or type a new value.

Both the ListBox and ComboBox controls can handle lists. In Visual Basic, a list is an array of strings formally referred to with the List property. (The List property is common to the ListBox and ComboBox controls.) Most work that you do with ListBoxes and ComboBoxes involves adding and removing strings from the List property. You can add strings to the List property of a ListBox or ComboBox at design time or runtime.

Add strings to a ListBox or ComboBox at design time
1. Add a ListBox or a ComboBox control to the form.

2. Select this control on the form.

3. In the Properties window, select the List property and type the strings for the List property in the drop-down box. To add multiple lines to the List property, press Ctrl+Enter to
add a new line to it

At design time, the strings you added to the List property will appear in the ListBox on the form .


This design-time method for populating the List property is useful when your program starts up. Because the contents of a ListBox or ComboBox can change frequently while your program is running, however, you need to be able to add and remove items to and from the List property while it's running. To do this, the ListBox and CombBox controls make extensive use of the AddItem, RemoveItem, and Clear methods.

To add a string to the List of a Listbox or ComboBox during runtime, use the AddItem method, which has the following syntax:

Object.AddItem StringToAdd

In this syntax,

Object is the Name property of the ListBox or ComboBox.

AddItem is the Visual Basic keyword for the method.

StringToAdd is the string that you want to add to the control's list.

Listing 3.13 shows you how to use the AddItem method in a form's Load event procedure to add strings to the List of a ListBox.

LISTING 3.13 11LIST04.TXT--Using the AddItem Method to Add Items to a ListBox Control

01 Private Sub Form_Load()
02 lstHero.AddItem "Superman"
03 lstHero.AddItem "Batman"
04 lstHero.AddItem "Green Lantern"
05 lstHero.AddItem "Aquaman"
06 lstHero.AddItem "SpiderMan"
07 lstHero.AddItem "Daredevil"
08 lstHero.AddItem "Hulk"
09 End Sub

Selecting Items from a List

To understand how Visual Basic determines the value of a string selected in the List of a ListBox or ComboBox, you need to understand that a List is an array of strings. As you learned earlier in this chapter, an array is declared as follows:

Dim ArrayName(Subscript) As DataType

Therefore, if you declared a four-element array as MyArray(3), you would list the elements in that array as follows:

MyArray(0)
MyArray(1)
MyArray(2)
MyArray(3)

If you want to determine the value assigned to the second element, you could use the following statement (remember, by default the first element is MyArray(0)):

MyValue = MyArray(1)

The ListBox and ComboBox controls use a similar format. The property List is the general term that Visual Basic uses to refer to the entire array of strings within a ListBox or ComboBox. Therefore, to find
the value of the second string in the ListBox called lstHero (from Listing 11.4), you would use the following statement:

SecondString$ = lstHero.List(1)

The value of SecondString$ is "Batman".

For a ListBox or ComboBox, the selected item in a list is contained in the ListIndex property. When you select a string in a ListBox, Visual Basic assigns the position number of that string to the ListIndex
property of the control. Therefore, to determine the value of the selected string in lstHero (from Listing 3.4), you would use the following code:

Private Sub lstHero_Click()
lblHero.Caption = lstHero.List(lstHero.ListIndex)
End Sub

This is the event procedure for the Click event of the lstHero ListBox control. When the user clicks a string in the ListBox, the code executes and reports back the value of the selection within the Caption property of the Label named lblHero. Figure 11.10 shows what happens when the user selects a string.


A faster way to find out the value of the string a user selects in a ListBox or ComboBox is to use the Text property. For example, you can use the following for a ListBox:

Dim strMyStr as String
strMyStr = List1.Text

For a ComboBox, use the following:

Dim strMyStr as String
strMyStr = Combo1.Text

Removing Items from a List


You remove a string from a list in a ListBox or ComboBox by using the RemoveItem method:

Object.RemoveItem Index

In this syntax

Object is the Name property of the ListBox or ComboBox.

RemoveItem is the Visual Basic keyword for the method used to remove items from a list.

Index is the position in the List property of the string that you want to remove from the control. To remove a selected item from a list, use the ListIndex property of the control.

Listing 3.14 shows the RemoveItem method used in code.


LISTING 3.14 LIST05.TXT--Using the RemoveItem Method to Remove a String
from a ListBox

01 Private Sub cmdRemove_Click()
02 lstHero.RemoveItem (lstHero.ListIndex)
03 lblHero.Caption = ""
04 End Sub

When you remove an item from a ListBox or ComboBox, make sure that you clear the text from the Caption property of the Label control lblHero, as shown in line 3 of Listing 3.14. If you don't do this, the user will remove the string from the ListBox, but it will remain in the Label control.


Clearing a List


If you want to remove all the strings contained in a ListBox or ComboBox, use the Clear method:

Object.Clear

In this syntax

Object is the Name property of the ListBox or ComboBox.

Clear is the Visual Basic keyword for the method that removes all items from a list.

lstHero.Clear

ComboBox Styles

Understanding ComboBox Styles

The ListBox and ComboBox controls have much in common, but each has a distinct use. A ListBox takes up more room than a ComboBox, and when using a ListBox, you can't select or input unlisted data. A ComboBox offers more flexibility and uses a form's space more efficiently.

A ComboBox's Style property enables you to change the operational characteristics and appearance of the control.

Using a simple combo ComboBox

When you first add a ComboBox with the 1 - Simple Combo style to a form, the ComboBox is sized so that none of the ListBox is displayed. Increase the Height property to display more of the ListBox.


Values for the ComboBox Style Property

0 - Drop-down Combo
A drop-down list. However, users can also enter new data to the ComboBox by inputting text directly into the TextBox portion of the ComboBox.
1 - Simple Combo
A combination of a TextBox and a ListBox that doesn't drop down. Users can select data from the ListBox or type new data into the TextBox. The size of a simple ComboBox
includes both edit and list portions.
2 - Drop-down List
A read-only drop-down list from which users can only select data. Users can't enter data into the control.

The most common ComboBox styles are 0 - Drop-down Combo and 2 - Drop-down List. As mentioned earlier, you use a drop-down ComboBox style when you want to let users add new data that's not in the ComboBox's list. You use the drop-down list style when you want users to choose from data that's only in the drop-down list of the ComboBox.

Using Arrays, ComboBoxes, and ListBoxes in a Sample Program


The Baseball ScoreKeeper program uses many things you've just learned. It uses arrays to keep track of each inning's score throughout the game and uses For...Next loops to traverse arrays to set and get particular values of an array's elements. It also uses arrays with a ListBox to present a team's roster and to display which player is now at bat.

To use the program, users pick the team now at bat by clicking the appropriate OptionButton at the left of the form. Then users can select the current player from a ListBox that lists the players on the team. At the end of an inning, users enter the inning number and the number of runs scored into two TextBoxes at the bottom of the form. Then users click the Add Runs button to display the runs scored during
the inning and the total score in the appropriate scoreboard and total score Label controls.


Examining ScoreKeeper's Event Procedures


The bulk of the work in the Baseball ScoreKeeper program takes place within the code of two event procedures, Form_Load() and cmdAddRun_Click().

How the Form_Load() procedure initializes the program

1. It declares local variables for scorekeeping and the scoreboard string and declares one array for each team's roster. Finally, it redimensions the two global scorekeeping arrays (these are declared in the module modBaseBall, one for each team) to nine elements. The elements of these arrays will hold the score for each inning.

2. It uses a For...Next loop to traverse each element in the Team One scorekeeping array, gTeamOneInnings(). Within each iteration of the loop, it takes the value in each element and adds it to the variable TotalScore%, which holds the total score for all the innings. Then Form_Load converts the value in each element to a character by using the CStr() function and combines this converted
character with the | character to form a larger scorekeeping string variable, InningString$ Next, the value of the InningString$ variable is assigned to the Caption property of the Scoreboard label, and the value of the TotalScore% variable is assigned to the Caption property of the total runs Label. Finally, the InningString$ variable is reset, and this step is repeated for Team Two.

3. It redimensions the team roster arrays for both teams to have nine elements. Then it adds the names and playing positions of each player to the element that corresponds to the player's position in the batting order.

4. It uses a For...Next loop with the AddItem method of each team's ListBox to traverse the roster arrays of each team and add the value of each element in the array (player name and playing position) to the respective ListBox.


Listing 3.15 shows these steps within the code of the Form_Load() event procedure.


LISTING 3.15 LIST.06.TXT--Initializing the Main Form of the Baseball
Scorekeeper Application with the Form_Load() Event Procedure

01 Private Sub Form_Load()
02 `=====================STEP ONE====================
03 Dim i% `Counter variable
04 Dim TotalScore%
05 Dim InningsString$ `String to display score for inning
06 Dim TeamOneRoster$() `Array to hold players' names
07 Dim TeamTwoRoster$() `Array to hold players' names
08 `Redimension the global score arrays
09 `DEFAULT_INNINGS is a constant declared in the module,
10 `modBaseBall
11 ReDim gTeamOneInnings(DEFAULT_INNINGS - 1)
12 ReDim gTeamTwoInnings(DEFAULT_INNINGS - 1)
13
14 `=====================STEP TWO====================
15 `Initialize the default score for Team One
16
17 TotalScore% = 0
18 For i% = 0 To DEFAULT_INNINGS - 1
19
20 InningsString$ = InningsString$ _
& CStr(gTeamOneInnings(i%)) & " | "
21 Next i%
22
23 `Display the concatenated string in the score label
24 lblTeamOne.Caption = InningsString$
25 `Display the total score for Team One
26 lblTeamOneScore.Caption = CStr(TotalScore%)
27
28 `Clean out score string for new trip through
29 `Team Two's score
30 InningsString$ = ""
31
32 `Initialize the default score for Team Two
33 For i% = 0 To DEFAULT_INNINGS - 1
34 InningsString$ = InningsString$ _
& CStr(gTeamTwoInnings(i%)) & " | "
35 Next i%
36 `Display the concatenated string in the score label
37 lblTeamTwo.Caption = InningsString$
38 `Display the total score for Team Two
39 lblTeamTwoScore.Caption = CStr(TotalScore%)
40 `=====================STEP THREE====================
41 `Make room in the roster arrays for 9 players
42 ReDim TeamOneRoster$(8) `Array elements begin at zero
43 ReDim TeamTwoRoster$(8)
44
45 `Add the players for Team One to the roster array
46 TeamOneRoster$(0) = "Phillips, lf"
47 TeamOneRoster$(1) = "Palmero, cf"
48 TeamOneRoster$(2) = "Erstad, 1b"
49 TeamOneRoster$(3) = "Hollins, 3b"
50 TeamOneRoster$(4) = "Salmon, rf"
51 TeamOneRoster$(5) = "Leyritz, dh"
52 TeamOneRoster$(6) = "Garne, c"
53 TeamOneRoster$(7) = "Gerbeck, 2b"
54 TeamOneRoster$(8) = "DiScensa, ss"
55
56 `Add the players for Team Two to the roster array
57 TeamTwoRoster$(0) = "Garciaparra, ss"
58 TeamTwoRoster$(1) = "Valentibn, 3b"
59 TeamTwoRoster$(2) = "Vaughn, 1b"
60 TeamTwoRoster$(3) = "Jefferson, dh"
61 TeamTwoRoster$(4) = "Cordero, lf"
62 TeamTwoRoster$(5) = "O'Leary, rf"
63 TeamTwoRoster$(6) = "Mack, cf"
64 TeamTwoRoster$(7) = "Hittenberg, c"
65 TeamTwoRoster$(8) = "Frey, 2b"
66
67 `=====================STEP FOUR====================
68 `Traverse the roster arrays and add the contents
69 `the the roster listboxes.
70 For i% = 0 To 8
71 lstTeamOne.AddItem (TeamOneRoster(i%))
72 lstTeamTwo.AddItem (TeamTwoRoster(i%))
73 Next i%
74
75 End Sub

The cmdAddRun_Click() event procedure is the code that adds the runs scored in a given inning to the Scoreboard when the user clicks the Add Runs button.


How the cmdAddRun_Click() procedure works
1. It creates variables that reflect the current inning and current score. It also creates a counter variable and variables that hold the total score and the scoreboard string.

Using IsNumeric()

IsNumeric() is a Visual Basic function that checks a string number to see whether it looks like a number. If the string looks like a number, IsNumeric() returns True; otherwise, it returns False.
IsNumeric() is often used to validate user input.

2. It takes the text entered in the Inning TextBox, inspects it to make sure that it's a number by using the IsNumeric() function, and makes sure that the user didn't enter a number higher than the
number of innings in the game so far. If this value is acceptable, the procedure assigns it to the current inning variable, CurrentInning%. cmdAddRun_Click() does the same sort of IsNumeric() check on the text in the Runs Scored TextBox, txtRuns. If this is acceptable, the procedure assigns it to the current score variable, CurrentScore%.

3. The procedure checks the value of the OptionButton for Team One. If the OptionButton is selected, its value will be True and the value in the current score variable, CurrentScore%, will be assigned to an element in gTeamOneInnings(), the global array that holds the values for Team One's per-inning score. The current score is assigned to the element position that's one less than the value of the current inning variable, CurrentInning%. This is because the first element in the gTeamOneInnings array is zero. If the value of the OptionButton for Team One is False, the OptionButton for Team Two must have been selected and this step should be done in terms of Team Two.

4. It traverses the global arrays that hold the value of every inning's score for each team to determine the total runs scored for each team. Then the procedure constructs the scoreboard string and assigns the results to the appropriate controls. (This is an exact repeat of the work done in the fourth step of the Form_Load() event procedure.)


Listing 3.16 shows the event procedure for the cmdAddRuns_Click event.


LISTING 3.16 LIST07.TXT--Determining the Team and Inning to Which the
Scored Runs Apply

01 Private Sub cmdAddRun_Click()
02 `=====================STEP One====================
03 Dim CurrentInning%
04 Dim CurrentScore%
05 Dim i%
06 Dim TotalScore%
07 Dim InningsString$
08
09 `=====================STEP TWO====================
10 `Convert the text in the txtInning to an Integer if
11 `indeed the text looks like a number
12 If IsNumeric(txtInning.Text) Then
13 CurrentInning% = CInt(txtInning.Text)
14 Else
15 CurrentInning% = 1
16 End If
17
18 `Make sure the inning number is not more than 9
19 If CurrentInning% > DEFAULT_INNINGS Then
20 CurrentInning% = DEFAULT_INNINGS
21 End If
22
23 `Convert the text in the txtRuns to an Integer if
24 `indeed the text looks like a number
25 If IsNumeric(txtRuns.Text) Then
26 CurrentScore% = CInt(txtRuns.Text)
27 Else
28 CurrentScore% = 0
29 End If
30 `=====================STEP THREE===================
31 `Set the score to the designated inning for the team
32 `identified by the check option box.
33 If opTeamOne.Value = True Then
34 gTeamOneInnings(CurrentInning% - 1) = CurrentScore%
35 Else
36 `If TeamOne.Value is not true, then TeamTwo.Value must
37 `be True. It's a logic thing!
38 gTeamTwoInnings(CurrentInning% - 1) = CurrentScore%
39 End If
40
41 `Set the new score for Team One
42 For i% = 0 To DEFAULT_INNINGS - 1
43 TotalScore% = TotalScore% + gTeamOneInnings(i%)
44 InningsString$ = InningsString$ _
& CStr(gTeamOneInnings(i%)) & " | "
45 Next i%
46 `=====================STEP FOUR===================
47 `Display the concatenated string in the score label
48 lblTeamOne.Caption = InningsString$
49 `Display the total score for Team One
50 lblTeamOneScore.Caption = CStr(TotalScore%)
51
52 `Clean out score string for new trip through
53 `Team Two's score
54 InningsString$ = ""
55 `Clean out the total score integer variable
56 TotalScore% = 0
57
58 `Set the new score for Team Two
59 For i% = 0 To DEFAULT_INNINGS - 1
60 TotalScore% = TotalScore% + gTeamTwoInnings(i%)
61 InningsString$ = InningsString$ _
& CStr(gTeamTwoInnings(i%)) & " | "
62 Next i%
63
64 `Display the concatenated string in the score label
65 lblTeamTwo.Caption = InningsString$
66 `Display the total score for Team One
67 lblTeamTwoScore.Caption = CStr(TotalScore%)
68
69 End Sub

The last thing that the program does is report who is at bat. This occurs in the Click event of the ListBox for each team's roster whenever the user selects a player. Listing 3.17 shows the code for Team One's ListBox Click event procedure. This code uses the List and ListIndex properties that you learned about in the section "Selecting Items from a List."


LISTING 3.17 11LIST08.TXT--Code for the lstTeamOne_Click Procedure

01 Private Sub lstTeamOne_Click()
02 `Have the name that the user clicks appear in the
03 `at bat label
04 lblTeamOneAtBat.Caption = lstTeamOne.List(lstTeamOne.ListIndex)
05 End Sub

The only difference between the lstTeamOne_Click and lstTeamTwo_Click event procedures is that the first references the Team One at bat Label and ListBox, and the other references the Team Two at
bat Label and ListBox.

Some potential problems still exist:

What happens if the user enters a letter character such as "a" in the Runs Scored or Inning text box? You need to provide an error check on these text boxes, possibly using the IsNumeric() function.

What if a game goes into extra innings? You need to Redim the scorekeeping arrays.

How do you remind users to change the player at bat if they forget, and what happens if users forget to change the team at bat? You might set a variable that tracks the most recently accessed player or team and provide a message box if this variable matches the new player or team when the Add Runs button is clicked.

You can easily solve these and other problems; for the most part, you possess the tools you need to address them. All that's required is a little thought and some experimentation.

Control Arrays

What Is a Control Array?


In Visual Basic, you can create arrays of any data type you want. You can also create an array of controls. Control arrays are a distinctive feature of Visual Basic that brings efficiency and power to the language. You can use them to create a common event procedure that's shared among all the controls in the control array. You also can use them to add and remove controls and forms to your program dynamically at runtime. This chapter shows you all you need to know to be able to work effectively with them.

Can I create a control array?

The name Index is used for other purposes, so check the online help before assuming that a control can be added to a control array.


All the intrinsic controls can be used in control arrays. These controls all have an Index property that's used to identify a particular control in a control array.


Creating a Control Array at Design Time


Many control arrays that you create will be built at design time. As you add controls to your form, you will need to group some of them into control arrays. This example shows you how to do that.

Create a control array of CommandButtons
1. Add a CommandButton to the center of the form frmMain and name it cmdMyButton. Set the value of cmdMyButton's Caption property to Action.

2. Make sure that cmdMyButton is selected. Choose Copy from the Edit menu to copy the CommandButton to the Clipboard.

3. Choose Paste from the Edit menu . You're presented with a dialog box asking whether you want to create a control array. Click Yes to create the control array.


Now that the control array is created, if you go to the Properties window and display the Object drop-down list, notice that there are now two CommandButtons with the name cmdMyButton, each with its own subscript.



Double-click either CommandButton to look at the Click event procedure. Notice that it now has an Index argument. This argument is an Integer that indicates the subscript of the control to which the event procedure applies. Because all controls of a control array share the same event procedure, you differentiate between controls by the value of Index--0 is the first control, 1 is the second control, 2 is the third, and so on.

The code in Listing 3.18 displays a string in the titlebar of the form frmMain that reports which CommandButton of the control array cmdMyButton() the user clicked. Copying this code to the cmdMyButton_Click(Index as Integer) event procedure can give you a sense of how to work with the Index argument.

LISTING 3.18 The CommandButton's Click Event Handler

01 Private Sub cmdMyButton_Click(Index As Integer)
02 ` Change the form's caption to indicate which
03 ` button in the control array generated an event.
04 Me.Caption = "You clicked button #" & Index & "."
05 End Sub




Extending Control Arrays at Runtime


Making a control array at design time will suffice if you know how many controls you will need in the array. But what do you do if you don't know how many controls you will need in your control array until the program is running? You solve this problem by adding controls to your control array at runtime by using the Load statement.

Add a control to a control array at runtime

1. Add a CommandButton to the upper-left corner of the form frmDArry and name it cmdCtrlArray.



Creating a control array

This action creates a control array with one element. The Index must be set to zero initially, so that controls loaded later will be added to the control array correctly.

2. In the Properties window, set the value of the CommandButton's Index property to 0.

3. So you can tell the controls apart at runtime, set the CommandButton's caption to Button #0.

4. Add the code in Listing 3.19 to the form's Form_Load() event.

5. Save and run the code.

LISTING 3.19 Adding a New CommandButton

01 Private Sub Form_Load()
02 `Create a new command button
03 Load cmdCtrlArray(1)
04
05 `Move it directly underneath the old one
06 cmdCtrlArray(1).Left = cmdCtrlArray(0).Left
07 cmdCtrlArray(1).Top = cmdCtrlArray(0).Top _
+ cmdCtrlArray(0).Height
08 cmdCtrlArray(1).Caption = "Button #1"
09
10 `Make the new button visible
11 cmdCtrlArray(1).Visible = True
12
13 End Sub

When you run the code, notice that the program makes a new CommandButton on the form and places it just below the first

Where did the control go?

All newly created elements of a control array have a Visible value of False. When you make your new controls at runtime, don't forget to put a line of code in that sets the value of the Visible property to True. Otherwise, you can't see the control.

You must do a certain amount of tweaking to get a newly created control to be operational in your program. New controls are exact duplicates of the first control element of the control array. The values of all properties except Index and Visible are identical--including the values of Left and Top. Thus, when you create a new control, it will be placed right over the first control in the array. For the new control
to be able to coexist with other controls in the control array, you must move the control to a new position.

Event Handler

Working with a Common Event Handler

As you saw in the preceding example, one benefit of control arrays is the ability to have a common event handler. This section features a program that allows users to input some numbers into a numeric telephone touch pad to place a call. Users also can set whether the call should be made by pulse or tone and can choose to send a fax or a simple voice call.

Don't worry if you don't know anything about telephony programming--you won't be writing any. This example is simply designed to show how control arrays could be used in this application.

This program uses a control array of CommandButtons to handle user input. Each keypad button is part of the cmdNum control array. Using a control array greatly simplifies matters. In this project, if you didn't use a control array, you would have 12 event procedures to program--not a very pleasant undertaking. However, when you use a control array, you have only one event procedure to program. You use the Index argument within the control array's one event procedure to figure out which control fired the event procedure.


LISTING 3.19 Keypad Event Handler

01 Private Sub cmdNum_Click(Index As Integer)
02 Dim strChar As String
03
04 `Find out which button was clicked by analyzing
05 the Index argument. Depending on which button
06 `you push, set the other string variable accordingly.
07 Select Case Index
08 `This button has the "*" character
09 Case 10
10 strChar = "*"
11 `This button has the "#" character
12 Case 11
13 strChar = "#"
14 `All the buttons have captions that match
15 `their index value.
16 Case Else
17 strChar = CStr(Index)
18 End Select
19
20 ` Add the new digit to the phone number.
21 lblNumber.Caption = lblNumber.Caption & strChar
22
23 End Sub


The only issue with this code is that each CommandButton's Index property must match exactly with its Caption property. For instance, this code assumes that the button marked as the one digit has a control array index of 1, the two button has an index of 2, and so on. If these buttons were deleted and re-created, they would have to be put back in order exactly or the code wouldn't work.

Listing 3.20 shows a revised version of this code, which still uses a single event handler but doesn't rely on the value of Index. Instead, it simply uses the value of the Caption property. It's also quite a bit shorter and more reliable.

LISTING 3.20 Revised Keypad Event Handler

01 Private Sub cmdNum_Click(Index As Integer)
02 lblNumber.Caption = lblNumber.Caption _
& cmdNum(Index).Caption
03 End Sub

Adding A Flex Grid Control

Adding A Flex Grid Control To A Program

The Program Design Department is calling. Can you whip up a program to display a few tables of data? No problem, you say, I_will use the Microsoft flex grid control. They ask, and how about spreadsheets? You say, no problem_flex grids can handle that too. You can add a flex grid to a Visual Basic project easily; just follow these steps:

1. Select the Project[vbar]Components menu item.
2. Click the Controls tab in the Components dialog box.
3. Select the Microsoft FlexGrid Control entry in the Components dialog box.
4. Close the Components dialog box by clicking on OK. This displays the Flex Grid Control tool in the toolbox.
5. Add a flex grid control to your form in the usual way for Visual Basic controls, using the Flex Grid Control tool.
6. Set the flex grid_s Rows and Cols properties to the number of rows and columns you want in your flex grid. You can also customize your flex grid by setting such properties as BorderStyle, ForeColor, BackColor, and so on.
This gives you a blank flex grid control in your program; the next step is to fill it with data. To start doing that, take a look at the next topic in this chapter.

TIP: When you insert a flex grid, you can also connect it to a database. To do this, you create a new data control (it_s an intrinsic Visual Basic control and appears in the toolbox when you start Visual Basic), connect that control to the database (by setting its DatabaseName and RecordSource properties), then set the flex grid_s DataSource property to the name of the data control. We_ll see more about connecting to a database when we discuss the data-bound Visual Basic controls. (See _Connecting A Flex Grid To A Database_ later in this chapter.)

Working With Data In A Flex Grid Control

You_re writing your new program, SuperDuperDataCrunch, and it_s time to write the code for the spreadsheet part. You can use a flex grid control here_but how do you insert and work with the data in a flex grid? To see how this works, we_ll build a small spreadsheet example program that adds a column of numbers. This will show how to insert and access data in a flex grid, as well as how to handle text insertion direct from the user in a rudimentary way (we_ll see a better method in the next topic in this chapter).
Several flex grid properties will help us here:

" Row_The current row in a flex grid
" Col_The current column in a flex grid
" Rows_The total number of rows
" Cols_The total number of columns
" Text_The text in the cell at (Row, Col)
We start by adding a flex grid to a form; give it 7 rows in the Rows property and 7 columns in the Cols property. We_ll begin by labeling the column heads with letters and the row heads with numbers, just as you would see in any spreadsheet program.

Flex grids have FixedCols and FixedRows properties, which set the header columns and rows in the flex grid. These columns and rows are meant to label the other columns and rows, and they appear in gray by default (the other cells are white by default). Both FixedCols and FixedRows are set to 1 by default.

We_will add a column of numbers here, so we can also place labels in the first column of cells, _Item 1_ to _Item 6_, and a label at the bottom, _Total_, to indicate that the bottom row holds the total of the six above. These labels are not necessary, of course, but we will add them to show that you can use text as well as numbers in a flex grid. These labels will appear in column 1 of the flex grid, and users can place the data they want to add in
column 2. The running sum appears at the bottom of column 2, as shown in

To set text in a flex grid cell, you set the Row and Col properties to that location and then place the text in the flex grid_s Text property. Here_s how we set up the row and column labels in MSFlexGrid1 when the form loads:
Sub Form_Load()
Dim Items(6) As String
Dim intLoopIndex As Integer
Items(1) = "Item 1"
Items(2) = "Item 2"
Items(3) = "Item 3"
Items(4) = "Item 4"
Items(5) = "Item 5"
Items(6) = "Total"
For intLoopIndex = 1 To MSFlexGrid1.Rows _ 1
MSFlexGrid1.Col = 0
MSFlexGrid1.Row = intLoopIndex
MSFlexGrid1.Text = Str(intLoopIndex)
MSFlexGrid1.Col = 1
MSFlexGrid1.Text = Items(intLoopIndex)
Next intLoopIndex
MSFlexGrid1.Row = 0
For intLoopIndex = 1 To MSFlexGrid1.Cols _ 1
MSFlexGrid1.Col = intLoopIndex
MSFlexGrid1.Text = Chr(Asc("A&") _ 1 + intLoopIndex)
Next intLoopIndex
MSFlexGrid1.Row = 1
MSFlexGrid1.Col = 1
End Sub

We_ve set up the labels as we want them_but what about reading data when the user types it? We can use the flex grid_s KeyPress event for that:

Sub MSFlexGrid1_KeyPress(KeyAscii As Integer)
End Sub

If the user enters numbers in the cells of column 2, we_ll add those values together in a running sum that appears at the bottom of that column, just as in a real spreadsheet program. To enter a number in a cell, the user can click the flex grid, which sets the grid_s Row and Col properties. Then, when the user types, we can add that text to the cell:

Sub MSFlexGrid1_KeyPress(KeyAscii As Integer)
MSFlexGrid1.Text = MSFlexGrid1.Text + Chr$(KeyAscii)
...
End Sub

This represents one way of letting the user enter text into a grid, but notice that we would have to handle all the editing and deleting functions ourselves this way; see the next topic in this chapter to see how to use a text box together with a flex grid for data entry. Now that the user has changed the data in the spreadsheet, we add the numbers in column 2 this way:


Sub MSFlexGrid1_KeyPress(KeyAscii As Integer)
Dim intRowIndex As Integer
Dim Sum As Integer
MSFlexGrid1.Text = MSFlexGrid1.Text + Chr$(KeyAscii)
MSFlexGrid1.Col = 2
Sum = 0
For intRowIndex = 1 To MSFlexGrid1.Rows _ 2
MSFlexGrid1.Row = intRowIndex
Sum = Sum + Val(MSFlexGrid1.Text)
Next intRowIndex
...
Note that each time you set the Row and Col properties to a new cell, that cell gets the focus. Because we want to place the sum of column 2 at the bottom of that column, that_s a problem. When we place the sum there, as users type the digits of the current number they are entering, the focus would keep moving to the bottom of the column. To avoid that, we save the current row and column and restore them when we are done displaying the sum:

Sub MSFlexGrid1_KeyPress(KeyAscii As Integer)
Dim intRowIndex As Integer
Dim Sum As Integer
MSFlexGrid1.Text = MSFlexGrid1.Text + Chr$(KeyAscii)
OldRow = MSFlexGrid1.Row
OldCol = MSFlexGrid1.Col
MSFlexGrid1.Col = 2
Sum = 0
For intRowIndex = 1 To MSFlexGrid1.Rows _ 2
MSFlexGrid1.Row = intRowIndex
Sum = Sum + Val(MSFlexGrid1.Text)
Next intRowIndex
MSFlexGrid1.Row = MSFlexGrid1.Rows _ 1
MSFlexGrid1.Text = Str(Sum)
MSFlexGrid1.Row = OldRow
MSFlexGrid1.Col = OldCol
End Sub
And that_s it. Now the user can type numbers into the spreadsheet, and we will display the running sum. We have created a spreadsheet program using a flex grid control.

Typing Data Into A Flex Grid
In the previous topic, we saw how to work with data in a flex grid and how to use the KeyPress event to support rudimentary text entry. Microsoft, however, suggests you use a text box for text entry in a flex grid_but how are you supposed to do that? The way you do it is to keep the text box invisible until the user selects a cell, then move the text box to that cell, size it to match the cell, and make it appear. When the user is done typing and clicks another cell, you transfer the text to the current cell and make the text box disappear.
Why Microsoft did not build this into flex grids is anybody’s guess perhaps because many flex grids are not supposed to support text entry, and that functionality would just take up memory. However, we can do it ourselves.
To see how this works, add a text box to a form, and set its Visible property to False so it starts off hidden.

Then add a flex grid to the form and give it, say, 10 columns and 10 rows. We can label the columns with letters and the rows with numbers, as is standard in spreadsheets (note that we use the Visual Basic Chr and Asc functions to set up the letters, and that we enter the text directly into the flex grid using its textArray property):
Sub Form_Load()
Dim intLoopIndex As Integer
For intLoopIndex = MSFlexGrid1.FixedRows To MSFlexGrid1.Rows _ 1
MSFlexGrid1.TextArray(MSFlexGrid1.Cols * intLoopIndex) =_
intLoopIndex
Next
For intLoopIndex = MSFlexGrid1.FixedCols To MSFlexGrid1.Cols _ 1
MSFlexGrid1.TextArray(intLoopIndex) = Chr(Asc("A") +_
intLoopIndex _ 1)
Next
End Sub
To select a cell, the user can click it with the mouse. When the user starts typing, we can add the text to the text box this way:
Sub MSFlexGrid1_KeyPress(KeyAscii As Integer)
Text1.Text = Text1.Text & Chr(KeyAscii)
Text1.SelStart = 1
...
We also move the text box to cover the current cell and shape it to match that cell using the flex grids CellLeft, CellTop, CellWidth, and CellHeight properties:
Sub MSFlexGrid1_KeyPress(KeyAscii As Integer)
Text1.Text = Text1.Text & Chr(KeyAscii)
Text1.SelStart = 1
Text1.Move MSFlexGrid1.CellLeft + MSFlexGrid1.Left,_
MSFlexGrid1.CellTop + MSFlexGrid1.Top, MSFlexGrid1.CellWidth,_
MSFlexGrid1.CellHeight
...
End Sub

Finally, we make the text box visible and give it the focus:
Sub MSFlexGrid1_KeyPress(KeyAscii As Integer)
Text1.Text = Text1.Text & Chr(KeyAscii)
Text1.SelStart = 1
Text1.Move MSFlexGrid1.CellLeft + MSFlexGrid1.Left,_
MSFlexGrid1.CellTop + MSFlexGrid1.Top, MSFlexGrid1.CellWidth,_
MSFlexGrid1.CellHeight
Text1.Visible = True
Text1.SetFocus
End Sub
When the user clicks another cell, a LeaveCell event is generated, and we can take advantage of that event to transfer the text from the text box to the current cell and hide the text box. Note that if the text box is not visible_in other words, the user is just moving around in the flex grid_we do not want to transfer the text from the text box to the current cell, and so we exit the procedure in that case:
Sub MSFlexGrid1_LeaveCell()
If Text1.Visible = False Then
Exit Sub
End If
...
Otherwise, we transfer the text from the text box to the current cell, clear the text box, and hide it:
Sub MSFlexGrid1_LeaveCell()
If Text1.Visible = False Then
Exit Sub
End If
MSFlexGrid1.Text = Text1
Text1.Visible = False
Text1.Text = ""
End Sub
And thats it. Now users can use the text box to enter text in a way that makes it look as though they are entering text directly into the flex grid


Setting Flex Grid Lines And Border Styles
You can set what types of grid lines a flex grid uses with the GridLines property. These can be set at design time or runtime to the following values:
" flexGridNone
" flexGridFlat
" flexGridInset
" flexGridRaised
You can set the grid line width with the GridLineWidth property.
In addition, you can set the BorderStyle property to show a border around the whole control, or no border at all:
" flexBorderNone
" flexBorderSingle
Labeling Rows And Columns In A Flex Grid
The usual convention in spreadsheets is to label the top row with letters and the first column with numbers.

Here some code to do just that (note that we use the Visual Basic Chr and Asc functions to set up the letters and enter text directly into the flex grid using its TextArray property, which holds the grid_s text in array form):
Sub Form_Load()
Dim intLoopIndex As Integer
For intLoopIndex = MSFlexGrid1.FixedRows To MSFlexGrid1.Rows _ 1
MSFlexGrid1.TextArray(MSFlexGrid1.Cols * intLoopIndex) =_
intLoopIndex
Next
For intLoopIndex = MSFlexGrid1.FixedCols To MSFlexGrid1.Cols _ 1
MSFlexGrid1.TextArray(intLoopIndex) = Chr(Asc("A") +_
intLoopIndex _ 1)
Next
End Sub

TIP: The columns and rows you label in a flex grid are usually colored gray; you set the number of label columns and rows with the FixedCols and FixedRows properties.



Formatting Flex Grid Cells
The Aesthetic Design Department is calling again. Can_t you use italics in that spreadsheet? Hmm, you think_can you?
Yes, you can: flex grid cells support formatting, including word wrap. You can format text using these properties of flex grids:
" CellFontBold
" CellFontItalic
" CellFontName
" CellFontUnderline
" CellFontStrikethrough
" CellFontSize
Besides the preceding properties, you can size cells as you like using the CellWidth and RowHeight properties.

Sorting A Flex Grid Control
The Testing Department is calling again. Your new program, SuperDuperDataCrunch, is terrific, but why can_t the user sort the data in your spreadsheet? Sounds like a lot of work, you think.

Actually, it_s easy. You just use the flex grid_s Sort property (available only at runtime). For example, to sort a flex grid according to the values in column 1 when the user clicks a button, add this code to your program (setting Sort to 1 sorts the flex grid on ascending values):

Private Sub Command1_Click()
MSFlexGrid1.Col = 1
MSFlexGrid1.Sort = 1
End Sub

TIP: Note that when the user clicks a column, that column becomes the new default column in the Col property, so if you want to let the user click a column and sort based on the values in that column, omit the MSFlexGrid1.Col = 1 in the preceding code.

Dragging Columns In A Flex Grid Control
One of the attractive aspects of flex grids is that you can use drag-and-drop with them to let users rearrange the flex grid as they like. To see how this works, we_ll write an example here that lets users drag and move columns around in a flex grid.

When the user presses the mouse button to start the drag operation, we store the column where the mouse went down in a form-wide variable named, say, intDragColumn in the MouseDown event. This event is stored in the flex grid_s MouseCol property:

Private Sub MSFlexGrid1_MouseDown(Button As Integer, Shift As Integer, _
X As Single, Y As Single)
intDragColumn = MSFlexGrid1.MouseCol
...
We also add that variable, intDragColumn, to the (General) declaration area of the form:
Dim intDragColumn As Integer
Then we start the drag and drop operation for the column in the flex grid:
Private Sub MSFlexGrid1_MouseDown(Button As Integer, Shift As Integer, _
X As Single, Y As Single)
intDragColumn = MSFlexGrid1.MouseCol
MSFlexGrid1.Drag 1
End Sub
Finally, when the user drags the column to a new location and drops it, we can catch that in the DragDrop event. In that events handler_s procedure, we move the column to its new location_the current mouse column_using the ColPosition property:

Private Sub MSFlexGrid1_DragDrop(Source As VB.Control, X As Single,
Y As Single)
MSFlexGrid1.ColPosition(intDragColumn) = MSFlexGrid1.MouseCol
End Sub
And that_s it. Now the user can drag and rearrange the columns in our flex grid. To see how this works, we display a database in our flex grid, as shown in Figure 12.17. To see how to do that, take a look at the next topic in this chapter where we use a Visual Basic data control (here, the database we use is the Nwind.mdb database, which comes with Visual Basic). When the user drags a column in our program, a special mouse pointer appears



Connecting A Flex Grid To A Database
We will work with databases later in this book, but because flex grids are often usedwith databases, we_ll take a look at how to connect a database to a flex grid here. To connect a database to a flex grid, follow these steps:
1. Add a data control, Data1, to your form (the data control is an intrinsic control in Visual Basic and appears in the toolbox when you start Visual Basic).
2. Set the data control_s DatabaseName property to the database file you want to use.
This can also be done at runtime, but if you do so, be sure to call the data controls
Refresh method to update that control. In code, the process goes something like this, where we use the Visual Basic App object_s Path property to get the applications path (assuming the database file is stored at the same path as the application):
Data1.DatabaseName = App.Path & "\Nwind.mdb"
Data1.Refresh
3. Set Data1. RecordSource property to the table in the database you want to work with.
4. Set the flex grid_s DataSource property to the data control_s name, which is Data1 here.

The Multiple Document Interface (MDI)

The Multiple Document Interface (MDI) was designed to simplify the exchange of information among documents, all under the same roof. With the main application, you can maintain multiple open windows, but not multiple copies of the application. Data exchange is easier when you can view and compare many documents simultaneously.
You almost certainly use Windows applications that can open multiple documents at the same time and allow the user to switch among them with a mouse-click. Multiple Word is a typical example, although most people use it in single document mode. Each document is displayed in its own window, and all document windows have the same behavior. The main Form, or MDI Form, isn't duplicated, but it acts as a container for all the windows, and it is called the parent window. The windows in which the individual documents are displayed are called Child windows.
An MDI application must have at least two Form, the parent Form and one or more child Forms. Each of these Forms has certain properties. There can be many child forms contained within the parent Form, but there can be only one parent Form.
The parent Form may not contain any controls. While the parent Form is open in design mode, the icons on the ToolBox are not displayed, but you can't place any controls on the Form. The parent Form can, and usually has its own menu.
To create an MDI application, follow these steps:
1. Start a new project and then choose Project >>> Add MDI Form to add the parent Form.
2. Set the Form's caption to MDI Window
3. Choose Project >>> Add Form to add a SDI Form.
4. Make this Form as child of MDI Form by setting the MDI Child property of the SDI Form to True. Set the caption property to MDI Child window.
Visual Basic automatically associates this new Form with the parent Form. This child Form can't exist outside the parent Form; in the words, it can only be opened within the parent Form.








Parent and Child Menus
MDI Form cannot contain objects other than child Forms, but MDI Forms can have their own menus. However, because most of the operations of the application have meaning only if there is at least one child Form open, there's a peculiarity about the MDI Forms. The MDI Form usually has a menu with two commands to load a new child Form and to quit the application. The child Form can have any number of commands in its menu, according to the application. When the child Form is loaded, the child Form's menu replaces the original menu on the MDI Form
Following example illustrates the above explanation.
* Open a new Project and name the Form as Menu.frm and save the Project as Menu.vbp
* Design a menu that has the following structure.
<> MDIMenu Menu caption
• MDIOpen opens a new child Form
• MDIExit terminates the application
* Then design the following menu for the child Form
<> ChildMenu Menu caption
• Child Open opens a new child Form
• Child Save saves the document in the active child Form
• Child Close Closes the active child Form
At design time double click on MDI Open and add the following code in the click event of the open menu.
Form1.Show
And so double click on MDI Exit and add the following code in the click event
End
Double click on Child Close and enter the following code in the click event
Unload Me
Before run the application in the project properties set MDI Form as the start-up Form. Save and run the application. Following output will be displayed.