You are on page 1of 133

1

Getting Started With Visual Basic 6.0 Visual Basic is initiated by using the Programs option > Microsoft Visual Basic 6.0 > Visual Basic 6.0. Clicking the Visual Basic icon, we can view a copyright screen enlisting the details of the license holder of the copy of Visual Basic 6.0. Then it opens in to a new screen as shown in figure 1 below, with the interface elements Such as MenuBar, ToolBar, The New Project dialog box. These elements permit the user to buid different types of Visual Basic applications. The Integrated Development Environment One of the most significant changes in Visual Basic 6.0 is the Integrated Development Environment (IDE). IDE is a term commonly used in the programming world to describe the interface and environment that we use to create our applications. It is calledintegrated because we can access virtually all of the development tools that we need from one screen called an interface. The IDE is also commonly referred to as the design environment, or theprogram. Tha Visual Basic IDE is made up of a number of components Menu Bar Tool Bar Project Explorer Properties window Form Layout Window Toolbox Form Designer Object Browser In previous versions of Visual Basic, the IDE was designed as a Single Document Interface (SDI). In a Single Document Interface, each window is a free-floating window that is contained within a main window and can move anywhere on the screen as long as Visual Basic is the current application. But, in Visual Basic 6.0, the IDE is in a Multiple Document Interface (MDI) format. In this format, the windows associated with the project will stay within a single container known as the parent. Code and form-based windows will stay within the main container form. Figure 1 The Visual Basic startup dialog box

Menu Bar This Menu Bar displays the commands that are required to build an application. The main menu items have sub menu items that can be chosen when needed. The toolbars in the menu bar provide quick access to the commonly used commands and a button in the toolbar is clicked once to carry out the action represented by it. Toolbox The Toolbox contains a set of controls that are used to place on a Form at design time thereby creating the user interface area. Additional controls can be included in the toolbox by using the Components menu item on the Project menu. A Toolbox is represented in figure 2 shown below.

Figure 2 Toolbox window with its controls available commonly.

Control Pointer PictureBox TextBox Frame CommandButton CheckBox OptionButton ListBox ComboBox HScrollBar and VScrollBar Timer DriveListBox

Description Provides a way to move and resize the controls form Displays icons/bitmaps and metafiles. It displays text or acts as a visual container for other controls. Used to display message and enter text. Serves as a visual and functional container for controls Used to carry out the specified action when the user chooses it. Displays a True/False or Yes/No option. OptionButton control which is a part of an option group allows the user to select only one option even it displays mulitiple choices. Displays a list of items from which a user can select one. Contains a TextBox and a ListBox. This allows the user to select an ietm from the dropdown ListBox, or to type in a selection in the TextBox. These controls allow the user to select a value within the specified range of values Executes the timer events at specified intervals of time Displays the valid disk drives and allows the user to select one of them.

DirListBox FileListBox Shape Line Image Data OLE Label

Allows the user to select the directories and paths, which are displayed. Displays a set of files from which a user can select the desired one. Used to add shape (rectangle, square or circle) to a Form Used to draw straight line to the Form used to display images such as icons, bitmaps and metafiles. But less capability than the PictureBox Enables the use to connect to an existing database and display information from it. Used to link or embed an object, display and manipulate data from other windows based applications. Displays a text that the user cannot modify or interact with.

Project Explorer Docked on the right side of the screen, just under the tollbar, is the Project Explorer window. The Project Explorer as shown in in figure servres as a quick reference to the various elements of a project namely form, classes and modules. All of the object that make up the application are packed in a project. A simple project will typically contain one form, which is a window that is designed as part of a program's interface. It is possible to develop any number of forms for use in a program, although a program may consist of a single form. In addition to forms, the Project Explorer window also lists code modules and classes. Figure 3 Project Explorer

Properties Window The Properties Window is docked under the Project Explorer window. The Properties Window exposes the various characteristics of selected objects. Each and every form in an application is considered an object. Now, each object in Visual Basic has characteristics such as color and size. Other characteristics affect not just the appearance of the object but the way it behaves too. All these characteristics of an object are called its properties. Thus, a form has properties and any controls placed on it will have propeties too. All of these properties are displayed in the Properties Window. Object Browser The Object Browser allows us to browse through the various properties, events and methods that are made available to us. It is accessed by selecting Object Browser from the View menu or pressing the key F2. The left column of the Object Browser lists the objects and classes that are available in the projects that are opened and the controls that have been referenced in them. It is possible for us to scroll through the list and select the object or class that we wish to inspect. After an object is picked up from the Classes list, we can see its members (properties, methods and events) in the right column. A property is represented by a small icon that has a hand holding a piece of paper. Methods are denoted by little green blocks, while events are denoted by yellow lightning bolt icon. Object naming conversions of controls (prefix) Form -frm Label-lbl TextBox-txt

CommandButton-cmd CheckBox -chk OptionButton -opt ComboBox -cbo ListBox-lst Frame-fme PictureBox -pic Image-img Shape-shp Line -lin HScrollBar -hsb VScrollBar -vsb Visual Basic 6.0 - Properties, Methods And Events All the controls in the ToolBox except the Pointer are objects in Visual Basic. These objects have associated properties, methods and events. Real world objects are loaded with properties. For example, a flower is loaded certain color, shape and fragrance. Similarly programming objects are loaded with properties. A property is a named attribute of a programming object. Properties define the characteristics of an object such as Size, Color etc. or sometimes the way in which it behaves. For example, a TextBox accepts properties such as Enabled, Font, MultiLine, Text, Visible, Width, etc. Enables property allows the TextBox to be enabled or disabled at run time depending on the condition set to True or False. Font property sets a particular font in the TextBox. MultiLine property allows the TextBox to accept and display multiple lines at run time. Text property of the TextBox control sets a particular text in the control. Visible property is used to hide the object at run time. Width property sets the TextBox to the desired width at design time. The properties that are discussed above are design-time properties that can be set at the design tme by selecting the Properties Window. But certain properties cannot be set at desgn time. For example, the CurrentX and CurrentY properties of a Form cannot be set at the design time. A method is an action that can be performed on objects. For example, a cat is an object. Its properties might include long white hair, blue eyes, 3 pounds weight etc. A complete definition of cat must only encompass on its looks, but should also include a complete itemization of its activities. Therefore, a cat's methods might be move, jump, play, breath etc. Siimilarly in object-orinted programming, a method is a connected or built-in procedure, a block of code that can be invoked to impart some action on a particular object. A method requires an object to provide them with a context. For example, the word Move has no meaning in Visual Basic, but the statement, Text1.Move 700, 400 performs a very precise action. The TextBox control has other associated methods such as Refresh, SetFocus, etc. The Refresh method enforces a complete repaint of the control or a Form. For example, Text1.Refresh refreshes the TextBox. The Setfocus method moves the focus on the control. For Example Text1.SetFocus sets the focus to TextBox control Text1. Event Driven Programming Visual Basic programs are built around events. Events are various things that can happen in a program. this will become clearer when studied in contrast to procedural programming. In procedural languages, an application is written is executed by checking for the program logically through the program statements, one after another. For a temporary phase, the control may be transferred to some other point in a program. While in an event driven application, the program statements are executed only when a particular event calls a specific part of the code that is assigned to the event. Let us consider a TextBox control and a few of its associated events to understand the concept of event driven programming. The TextBox control supports various events such as Change, Click, MouseMove and many more that will be listed in the Properties dropdown list in the code window for the TextBox control. We will look into a few of them as given below.

The code entered in the Change event fires when there is a change in the contents of the TextBox The Click event fires when the TextBox control is clicked. The MouseMove event fires when the mouse is moved over the TextBox

As explained above, several events are associated with different controls and forms, some of the events being common to most of them and few being specific to each control.

Visual Basic 6 (VB6) Data Types, Modules And Operators Visual Basic uses building blocks such as Variables, Data Types, Procedures, Functions and Control Structures in its programming environment. This section concentrates on the programming fundamentals of Visual Basic with the blocks specified. Modules Code in Visual Basic is stored in the form of modules. The three kind of modules are Form Modules, Standard Modules and Class Modules. A simple application may contain a single Form, and the code resides in that Form module itself. As the application grows, additional Forms are added and there may be a common code to be executed in several Forms. To avoid the duplication of code, a separate module containing a procedure is created that implements the common code. This is a standard Module. Class module (.CLS filename extension) are the foundation of the object oriented programming in Visual Basic. New objects can be created by writing code in class modules. Each module can contain: Declarations : May include constant, type, variable and DLL procedure declarations. Procedures : A sub function, or property procedure that contain pieces of code that can be executed as a unit. These are the rules to follow when naming elements in VB - variables, constants, controls, procedures, and so on: A name must begin with a letter. May be as much as 255 characters long (but don't forget that somebody has to type the stuff!). Must not contain a space or an embedded period or type-declaration characters used to specify a data type; these are ! # % $ & @ Must not be a reserved word (that is part of the code, like Option, for example) The dash, although legal, should be avoided because it may be confused with the minus sign. Instead of First-name use First_name or FirstName. Data types in Visual Basic 6 By default Visual Basic variables are of variant data types. The variant data type can store numeric, date/time or string data. When a variable is declared, a data type is supplied for it that determines the kind of data they can store. The fundamental data types in Visual Basic including variant are integer, long, single, double, string, currency, byte and boolean. Visual Basic supports a vast array of data types. Each data type has limits to the kind of information and the minimum and maximum values it can hold. In addition, some types can interchange with some other types. A list of Visual Basic's simple data types are given below. 1. Numeric Byte Integer Long Single Double Currency Store integer values in the range of 0 - 255 Store integer values in the range of (-32,768) - (+ 32,767) Store integer values in the range of (- 2,147,483,468) - (+ 2,147,483,468) Store floating point value in the range of (-3.4x10-38) - (+ 3.4x1038) Store large floating value which exceeding the single data type value store monetary values. It supports 4 digits to the right of decimal point and 15 digits to the left

2. String Use to store alphanumeric values. A variable length string can store approximately 4 billion characters 3. Date Use to store date and time values. A variable declared as date type can store both date and time values and it can store date values 01/01/0100 up to 12/31/9999 4. Boolean Boolean data types hold either a true or false value. These are not stored as numeric values and cannot be used as such. Values are internally stored as -1 (True) and 0 (False) and any non-zero value is considered as true. 5. Variant Stores any type of data and is the default Visual Basic data type. In Visual Basic if we declare a variable without any data type by default the data type is assigned as default.

Operators in Visual Basic Arithmetical Operators Operators + / \ * ^ Mod & Add Substract Divide Integer Division Multiply Exponent (power of) Remainder of division String concatenation Description 5+5 10-5 25/5 20\3 5*4 3^3 20 Mod 6 "George"&" "&"Bush" Example 10 5 5 6 20 27 2 "George Bush" Result

Relational Operators Operators > < >= <= <> = Description Greater than Less than Greater than or equal to Less than or equal to Not Equal to Equal to 10>8 10<8 20>=10 10<=20 5<>4 5=7 Example True False True True True False Result

Logical Operators Operators OR AND Description Operation will be true if either of the operands is true Operation will be true only if both the operands are true

Variables In Visual Basic 6 Variables are the memory locations which are used to store values temporarily. A defined naming strategy has to be followed while naming a variable. A variable name must begin with an alphabet letter and should not exceed 255 characters. It must be unique within the same scope. It should not contain any special character like %, &, !, #, @ or $. There are many ways of declaring variables in Visual Basic. Depending on where the variables are declared and how they are declared, we can determine how they can be used by our application. The different ways of declaring variables in Visual Basic are listed below and elucidated in this section. Explicit Declaration Using Option Explicit statement Scope of Variables

Explicit Declaration Declaring a variable tells Visual Basic to reserve space in memory. It is not must that a variable should be declared before using it. Automatically whenever Visual Basic encounters a new variable, it assigns the default variable type and value.

This is called implicit declaration. Though this type of declaration is easier for the user, to have more control over the variables, it is advisable to declare them explicitly. The variables are declared with a Dim statement to name the variable and its type. The As type clause in the Dim statement allows to define the data type or object type of the variable. This is called explicit declaration. Syntax Dim variable [As Type] For example, Dim strName As String Dim intCounter As Integer Using Option Explicit statement It may be convenient to declare variables implicitly, but it can lead to errors that may not be recognized at run time. Say, for example a variable by name intcount is used implicitly and is assigned to a value. In the next step, this field is incremented by 1 by the following statement Intcount = Intcount + 1 This calculation will result in intcount yielding a value of 1 as intcount would have been initialized to zero. This is because the intcount variable has been mityped as incont in the right hand side of the second variable. But Visual Basic does not see this as a mistake and considers it to be new variable and therefore gives a wrong result. In Visual Basic, to prevent errors of this nature, we can declare a variable by adding the following statement to the general declaration section of the Form. Option Explicit This forces the user to declare all the variables. The Option Explicit statement checks in the module for usage of any undeclared variables and reports an error to the user. The user can thus rectify the error on seeing this error message. The Option Explicit statement can be explicitly placed in the general declaration section of each module using the following steps. Click Options item in the Tools menu Click the Editor tab in the Options dialog box Check Require Variable Declaration option and then click the OK button Scope of variables A variable is scoped to a procedure-level (local) or module-level variable depending on how it is declared. The scope of a variable, procedure or object determines which part of the code in our application are aware of the variable's existence. A variable is declared in general declaration section of e Form, and hence is available to all the procedures. Local variables are recognized only in the procedure in which they are declared. They can be declared with Dim and Static keywords. If we want a variable to be available to all of the procedures within the same module, or to all the procedures in an application, a variable is declared with broader scope. Local Variables A local variable is one that is declared inside a procedure. This variable is only available to the code inside the procedure and can be declared using the Dim statements as given below. Dim sum As Integer The local variables exist as long as the procedure in which they are declared, is executing. Once a procedure is executed, the values of its local variables are lost and the memory used by these variables is freed and can be reclaimed. Variables that are declared with keyword Dim exist only as long as the procedure is being executed. Static Variables Static variables are not reinitialized each time Visual Invokes a procedure and therefore retains or preserves value even when a procedure ends. In case we need to keep track of the number of times a command button in an application is clicked, a static counter variable has to be declared. These static variables are also ideal for making controls alternately visible or invisible. A static variable is declared as given below. Static intPermanent As Integer Variables have a lifetime in addition to scope. The values in a module-level and public variables are preserved for the lifetime of an application whereas local variables declared with Dim exist only while the procedure in which they are declared is still being executed. The value of a local variable can be preserved using the Static keyword. The follwoing procedure calculates the running total by adding new values to the previous values stored in the static variable value. Function RunningTotal ( ) Static Accumulate Accumulate = Accumulate + num

RunningTotal = Accumulate End Function If the variable Accumulate was declared with Dim instead of static, the previously accumulated values would not be preserved accross calls to the procedure, and the procedure would return the same value with which it was called. To make all variables in a procedure static, the Static keyword is placed at the beginning of the procedure heading as given in the below statement. Static Function RunningTotal ( ) Example The following is an example of an event procedure for a CommandButton that counts and displays the number of clicks made. Private Sub Command1_Click ( ) Static Counter As Integer Counter = Counter + 1 Print Counter End Sub The first time we click the CommandButton, the Counter starts with its default value of zero. Visual Basic then adds 1 to it and prints the result. Module Levele Variables A module level variable is available to all the procedures in the module. They are declared using the Public or the Privatekeyword. If you declare a variable using a Private or a Dim statement in the declaration section of a modulea standard BAS module, a form module, a class module, and so onyou're creating a private module-level variable. Such variables are visible only from within the module they belong to and can't be accessed from the outside. In general, these variables are useful for sharing data among procedures in the same module: ' In the declarative section of any module Private LoginTime As Date ' A private module-level variable Dim LoginPassword As String ' Another private module-level variable You can also use the Public attribute for module-level variables, for all module types except BAS modules. (Public variables in BAS modules are global variables.) In this case, you're creating a strange beast: a Public module-level variable that can be accessed by all procedures in the module to share data and that also can be accessed from outside the module. In this case, however, it's more appropriate to describe such a variable as a property: ' In the declarative section of Form1 module Public CustomerName As String ' A Public property You can access a module property as a regular variable from inside the module and as a custom property from the outside: ' From outside Form1 module... Form1.CustomerName = "John Smith" The lifetime of a module-level variable coincides with the lifetime of the module itself. Private variables in standard BAS modules live for the entire life of the application, even if they can be accessed only while Visual Basic is executing code in that module. Variables in form and class modules exist only when that module is loaded in memory. In other words, while a form is active (but not necessarily visible to the user) all its variables take some memory, and this memory is released only when the form is completely unloaded from memory. The next time the form is re-created, Visual Basic reallocates memory for all variables and resets them to their default values (0 for numeric values, "" for strings, Nothing for object variables). Public vs Local Variables A variable can have the same name and different scope. For example, we can have a public variable named R and within a procedure we can declare a local variable R. References to the name R within the procedure would access the local variable and references to R outside the procedure would access the public variable. Procedures In Visual Basic 6

Visual Basic offers different types of procedures to execute small sections of coding in applications. The various procedures are elucidated in details in this section. Visual Basic programs can be broken into smaller logical components called Procedures. Procedures are useful for condensing repeated operations such as the frequently used calculations, text and control manipulation etc. The benefits of using procedures in programming are: It is easier to debug a program a program with procedures, which breaks a program into discrete logical limits. Procedures used in one program can act as building blocks for other programs with slight modifications. A Procedure can be Sub, Function or Property Procedure. Sub Procedures A sub procedure can be placed in standard, class and form modules. Each time the procedure is called, the statements between Sub and End Sub are executed. The syntax for a sub procedure is as follows: [Private | Public] [Static] Sub Procedurename [( arglist)] [ statements] End Sub arglist is a list of argument names separated by commas. Each argument acts like a variable in the procedure. There are two types of Sub Procedures namely general procedures and event procedures. Event Procedures An event procedure is a procedure block that contains the control's actual name, an underscore(_), and the event name. The following syntax represents the event procedure for a Form_Load event. Private Sub Form_Load() ....statement block.. End Sub Event Procedures acquire the declarations as Private by default. General Procedures A general procedure is declared when several event procedures perform the same actions. It is a good programming practice to write common statements in a separate procedure (general procedure) and then call them in the event procedure. In order to add General procedure: The Code window is opened for the module to which the procedure is to be added. The Add Procedure option is chosen from the Tools menu, which opens an Add Procedure dialog box as shown in the figure given below. The name of the procedure is typed in the Name textbox Under Type, Sub is selected to create a Sub procedure, Function to create a Function procedure or Property to create a Property procedure. Under Scope, Public is selected to create a procedure that can be invoked outside the module, or Private to create a procedure that can be invoked only from within the module.

We can also create a new procedure in the current module by typing Sub ProcedureName, Function ProcedureName, or Property ProcedureName in the Code window. A Function procedure returns a value and a Sub Procedure does not return a value.

10

Function Procedures Functions are like sub procedures, except they return a value to the calling procedure. They are especially useful for taking one or more pieces of data, called arguments and performing some tasks with them. Then the functions returns a value that indicates the results of the tasks complete within the function. The following function procedure calculates the third side or hypotenuse of a right triangle, where A and B are the other two sides. It takes two arguments A and B (of data type Double) and finally returns the results. Function Hypotenuse (A As Double, B As Double) As Double Hypotenuse = sqr (A^2 + B^2) End Function The above function procedure is written in the general declarations section of the Code window. A function can also be written by selecting the Add Procedure dialog box from the Tools menu and by choosing the required scope and type. Property Procedures A property procedure is used to create and manipulate custom properties. It is used to create read only properties for Forms, Standard modules and Class modules.Visual Basic provides three kind of property procedures-Property Let procedure that sets the value of a property, Property Get procedure that returns the value of a property, and Property Set procedure that sets the references to an object. Control Structures In Visual Basic 6.0 Control Statements are used to control the flow of program's execution. Visual Basic supports control structures such as if... Then, if...Then ...Else, Select...Case, and Loop structures such as Do While...Loop, While...Wend, For...Next etc method. If...Then selection structure The If...Then selection structure performs an indicated action only when the condition is True; otherwise the action is skipped. Syntax of the If...Then selection If <condition> Then statement End If e.g.: If average>75 Then txtGrade.Text = "A" End If If...Then...Else selection structure The If...Then...Else selection structure allows the programmer to specify that a different action is to be performed when the condition is True than when the condition is False. Syntax of the If...Then...Else selection If <condition > Then statements Else statements End If e.g.: If average>50 Then txtGrade.Text = "Pass" Else txtGrade.Text = "Fail" End If

11

Nested If...Then...Else selection structure Nested If...Then...Else selection structures test for multiple cases by placing If...Then...Else selection structures inside If...Then...Else structures. Syntax of the Nested If...Then...Else selection structure You can use Nested If either of the methods as shown above Method 1 If < condition 1 > Then statements ElseIf < condition 2 > Then statements ElseIf < condition 3 > Then statements Else Statements End If Method 2 If < condition 1 > Then statements Else If < condition 2 > Then statements Else If < condition 3 > Then statements Else Statements End If End If EndIf e.g.: Assume you have to find the grade using nested if and display in a text box If average > 75 Then txtGrade.Text = "A" ElseIf average > 65 Then txtGrade.Text = "B" ElseIf average > 55 Then txtGrade.text = "C" ElseIf average > 45 Then txtGrade.Text = "S" Else txtGrade.Text = "F" End If

12

Select...Case selection structure Select...Case structure is an alternative to If...Then...ElseIf for selectively executing a single block of statements from among multiple block of statements. Select...case is more convenient to use than theIf...Else...End If. The following program block illustrate the working of Select...Case. Syntax of the Select...Case selection structure Select Case Index Case 0 Statements Case 1 Statements End Select e.g.: Assume you have to find the grade using select...case and display in the text box Dim average as Integer average = txtAverage.Text Select Case average Case 100 To 75 txtGrade.Text ="A" Case 74 To 65 txtGrade.Text ="B" Case 64 To 55 txtGrade.Text ="C" Case 54 To 45 txtGrade.Text ="S" Case 44 To 0 txtGrade.Text ="F" Case Else MsgBox "Invalid average marks" End Select Note: In this example I have used a message box function. In later lessons you will learn how to use message box functions. Loops (Repetition Structures) In Visual Basic 6 A repetition structure allows the programmer to that an action is to be repeated until given condition is true. Do While... Loop Statement The Do While...Loop is used to execute statements until a certain condition is met. The following Do Loop counts from 1 to 100. Dim number As Integer number = 1 Do While number <= 100 number = number + 1 Loop A variable number is initialized to 1 and then the Do While Loop starts. First, the condition is tested; if condition is True, then the statements are executed. When it gets to the Loop it goes back to the Do and tests condition again. If condition is False on the first pass, the statements are never executed.

13

While... Wend Statement A While...Wend statement behaves like the Do While...Loop statement. The following While...Wend counts from 1 to 100 Dim number As Integer number = 1 While number <=100 number = number + 1 Wend Do...Loop While Statement The Do...Loop While statement first executes the statements and then test the condition after each execution. The following program block illustrates the structure: Dim number As Long number = 0 Do number = number + 1 Loop While number < 201 The programs executes the statements between Do and Loop While structure in any case. Then it determines whether the counter is less than 501. If so, the program again executes the statements between Do and Loop While else exits the Loop. Do Until...Loop Statement Unlike the Do While...Loop and While...Wend repetition structures, the Do Until... Loop structure tests a condition for falsity. Statements in the body of a Do Until...Loop are executed repeatedly as long as the loop-continuation test evaluates to False. An example for Do Until...Loop statement. The coding is typed inside the click event of the command button Dim number As Long number=0 Do Until number > 1000 number = number + 1 Print number Loop Numbers between 1 to 1000 will be displayed on the form as soon as you click on the command button. The For...Next Loop The For...Next Loop is another way to make loops in Visual Basic. For...Next repetition structure handles all the details of counter-controlled repetition. The following loop counts the numbers from 1 to 100: Dim x As Integer For x = 1 To 50 Print x Next

14

In order to count the numbers from 1 yo 50 in steps of 2, the following loop can be used For x = 1 To 50 Step 2 Print x Next The following loop counts numbers as 1, 3, 5, 7..etc The above coding will display numbers vertically on the form. In order to display numbers horizontally the following method can be used. For x = 1 To 50 Print x & Space$ (2); Next To increase the space between the numbers increase the value inside the brackets after the & Space$. Following example is a For...Next repetition structure which is with the If condition used. Dim number As Integer For number = 1 To 10 If number = 4 Then Print "This is number 4" Else Print number End If Next In the output instead of number 4 you will get the "This is number 4". Exit For And Exit Do Statement In Visual Basic 6 A For...Next loop condition can be terminated by an Exit Forstatement. Consider the following statement block. Dim x As Integer For x = 1 To 10 Print x If x = 5 Then Print "The program exited at x=5" Exit For End If Next The preceding code increments the value of x by 1 until it reaches the condition x = 5. The Exit For statement is executed and it terminates the For...Next loop. The Following statement block containing Do...While loop is terminated using Exit Do statement. Dim x As Integer Do While x < 10 Print x x=x+1 If x = 5 Then Print "The program is exited at x=5" Exit Do End If Loop

15

With...End With statement When properties are set for objects or methods are called, a lot of coding is included that acts on the same object. It is easier to read the code by implementing the With...End With statement. Multiple properties can be set and multiple methods can be called by using the With...End With statement. The code is executed more quickly and efficiently as the object is evaluated only once. The concept can be clearly understood with following example. With Text1 .Font.Size = 14 .Font.Bold = True .ForeColor = vbRed .Height = 230 .Text = "Hello World" End With In the above coding, the object Text1, which is a text box is evaluated only once instead of every associated property or method. This makes the coding simpler and efficient. VB Array - Arrays In Visual Basic 6 An array is a consecutive group of memory locations that all have the same name and the same type. To refer to a particular location or element in the array, we specify the array name and the array element position number. The Individual elements of an array are identified using an index. Arrays have upper and lower bounds and the elements have to lie within those bounds. Each index number in an array is allocated individual memory space and therefore users must evade declaring arrays of larger size than required. We can declare an array of any of the basic data types including variant, user-defined types and object variables. The individual elements of an array are all of the same data type. Declaring arrays Arrays occupy space in memory. The programmer specifies the array type and the number of elements required by the array so that the compiler may reserve the appropriate amount of memory. Arrays may be declared as Public (in a code module), module or local. Module arrays are declared in the general declarations using keyword Dim or Private. Local arrays are declared in a procedure using Dim or Static. Array must be declared explicitly with keyword "As". There are two types of arrays in Visual Basic namely: Fixed-size array : The size of array always remains the same-size doesn't change during the program execution. Dynamic array : The size of the array can be changed at the run time- size changes during the program execution. Fixed-sized Arrays When an upper bound is specified in the declaration, a Fixed-array is created. The upper limit should always be within the range of long data type. Declaring a fixed-array Dim numbers(5) As Integer In the above illustration, numbers is the name of the array, and the number 6 included in the parentheses is the upper limit of the array. The above declaration creates an array with 6 elements, with index numbers running from 0 to 5. If we want to specify the lower limit, then the parentheses should include both the lower and upper limit along with the To keyword. An example for this is given below.

16

Dim numbers (1 To 6 ) As Integer In the above statement, an array of 10 elements is declared but with indexes running from 1 to 6. A public array can be declared using the keyword Public instead of Dim as shown below. Public numbers(5) As Integer Multidimensional Arrays Arrays can have multiple dimensions. A common use of multidimensional arrays is to represent tables of values consisting of information arranged in rows and columns. To identify a particular table element, we must specify two indexes: The first (by convention) identifies the element's row and the second (by convention) identifies the element's column. Tables or arrays that require two indexes to identify a particular element are called two dimensional arrays. Note that multidimensional arrays can have more than two dimensions. Visual Basic supports at least 60 array dimensions, but most people will need to use more than two or three dimensional-arrays. The following statement declares a two-dimensional array 50 by 50 array within a procedure. Dim AvgMarks ( 50, 50) It is also possible to define the lower limits for one or both the dimensions as for fixed size arrays. An example for this is given here. Dim Marks ( 101 To 200, 1 To 100) An example for three dimensional-array with defined lower limits is given below. Dim Details( 101 To 200, 1 To 100, 1 To 100) Static and dynamic arrays Basically, you can create either static or dynamic arrays. Static arrays must include a fixed number of items, and this number must be known at compile time so that the compiler can set aside the necessary amount of memory. You create a static array using a Dim statement with a constant argument: ' This is a static array. Dim Names(100) As String Visual Basic starts indexing the array with 0. Therefore, the preceding array actually holds 101 items. Most programs don't use static arrays because programmers rarely know at compile time how many items you need and also because static arrays can't be resized during execution. Both these issues are solved by dynamic arrays. You declare and create dynamic arrays in two distinct steps. In general, you declare the array to account for its visibility (for example, at the beginning of a module if you want to make it visible by all the procedures of the module) using a Dim command with an empty pair of brackets. Then you create the array when you actually need it, using a ReDim statement: ' An array defined in a BAS module (with Private scope) Dim Customers() As String ... Sub Main() ' Here you create the array. ReDim Customer(1000) As String End Sub

17

If you're creating an array that's local to a procedure, you can do everything with a single ReDim statement: Sub PrintReport() ' This array is visible only to the procedure. ReDim Customers(1000) As String ' ... End Sub If you don't specify the lower index of an array, Visual Basic assumes it to be 0, unless an Option Base 1 statement is placed at the beginning of the module. My suggestion is this: Never use an Option Base statement because it makes code reuse more difficult. (You can't cut and paste routines without worrying about the current Option Base.) If you want to explicitly use a lower index different from 0, use this syntax instead: ReDim Customers(1 To 1000) As String Dynamic arrays can be re-created at will, each time with a different number of items. When you re-create a dynamic array, its contents are reset to 0 (or to an empty string) and you lose the data it contains. If you want to resize an array without losing its contents, use the ReDim Preserve command: ReDim Preserve Customers(2000) As String When you're resizing an array, you can't change the number of its dimensions nor the type of the values it contains. Moreover, when you're using ReDim Preserve on a multidimensional array, you can resize only its last dimension: ReDim Cells(1 To 100, 10) As Integer ... ReDim Preserve Cells(1 To 100, 20) As Integer ' This works. ReDim Preserve Cells(1 To 200, 20) As Integer ' This doesn't. Finally, you can destroy an array using the Erase statement. If the array is dynamic, Visual Basic releases the memory allocated for its elements (and you can't read or write them any longer); if the array is static, its elements are set to 0 or to empty strings. You can use the LBound and UBound functions to retrieve the lower and upper indices. If the array has two or more dimensions, you need to pass a second argument to these functions to specify the dimension you need: Print LBound(Cells, 1) ' Displays 1, lower index of 1st dimension Print LBound(Cells) ' Same as above Print UBound(Cells, 2) ' Displays 20, upper index of 2nd dimension ' Evaluate total number of elements. NumEls = (UBound(Cells) _ LBound(Cells) + 1) * _ (UBound(Cells, 2) _ LBound(Cells, 2) + 1) Arrays within UDTs UDT structures can include both static and dynamic arrays. Here's a sample structure that contains both types: Type MyUDT StaticArr(100) As Long DynamicArr() As Long End Type ... Dim udt As MyUDT ' You must DIMension the dynamic array before using it. ReDim udt.DynamicArr(100) As Long ' You don't have to do that with static arrays. udt.StaticArr(1) = 1234

18

The memory needed by a static array is allocated within the UDT structure; for example, the StaticArr array in the preceding code snippet takes exactly 400 bytes. Conversely, a dynamic array in a UDT takes only 4 bytes, which form a pointer to the memory area where the actual data is stored. Dynamic arrays are advantageous when each individual UDT variable might host a different number of array items. As with all dynamic arrays, if you don't dimension a dynamic array within a UDT before accessing its items, you get an error 9"Subscript out of range." User-Defined Data Types In Visual Basic 6 Variables of different data types when combined as a single variable to hold several related informations is called a UserDefined data type. A Type statement is used to define a user-defined type in the General declaration section of a form or module. Userdefined data types can only be private in form while in standard modules can be public or private. An example for a user defined data type to hold the product details is as given below. Private Type ProductDetails ProdID as String ProdName as String Price as Currency End Type The user defined data type can be declared with a variable using the Dim statement as in any other variable declaration statement. An array of these user-defined data types can also be declared. An example to consolidate these two features is given below. Dim ElectronicGoods as ProductDetails ' One Record Dim ElectronicGoods(10) as ProductDetails ' An array of 11 records A User-Defined data type can be referenced in an application by using the variable name in the procedure along with the item name in the Type block. Say, for example if the text property of a TextBox namely text1 is to be assigned the name of the electronic good, the statement can be written as given below. Text1.Text = ElectronicGoods.ProdName If the same is implemented as an array, then the statement becomes Text1.Text = ElectronicGoods(i).ProdName User-defined data types can also be passed to procedures to allow many related items as one argument. Sub ProdData( ElectronicGoods as ProductDetails) Text1.Text = ElectronicGoods.ProdName Text1.Text = ElectronicGoods.Price End Sub Constants, Data Type Conversion, Visual Basic Built-In Functions Constants Constants are named storage locations in memory, the value of which does not change during program Execution. They remain the same throughout the program execution. When the user wants to use a value that never changes, a constant can be declared and created. The Const statement is used to create a constant. Constants can be declared in local, form, module or global scope and can be public or private as for variables. Constants can be declared as illustrated below. Public Const gravityconstant As Single = 9.81

19

Predefined Visual Basic Constants The predefined constants can be used anywhere in the code in place of the actual numeric values. This makes the code easier to read and write. For example consider a statement that will set the window state of a form to be maximized. Form1.Windowstate = 2 The same task can be performed using a Visual Basic constant Form1.WindowState = vbMaximized Data Type Conversion Visual Basic functions either to convert a string into an integer or vice versa and many more conversion functions. A complete listing of all the conversion functions offered by Visual Basic is elucidated below. Conversion To Boolean Byte Currency Date Decimals Double Integer Long Single String Variant Error Function Cbool Cbyte Ccur Cdate Cdec CDbl Cint CLng CSng CStr Cvar CVErr

A conversion function should always be placed at the right hand side of the calculation statement.

20

Visual Basic Built-in Functions Many built-in functions are offered by Visual Basic fall under various categories. These functions are procedures that return a value. The functions fall into the following basic categories that will be discussed in the follwing sections at length. Date and Time Functions Format Function String Functions Date And Time Functions In Visual Basic 6 Not only does Visual Basic let you store date and time information in the specific Date data type, it also provides a lot of date- and time-related functions. These functions are very important in all business applications and deserve an in-depth look. Date and Time are internally stored as numbers in Visual Basic. The decimal points represents the time between 0:00:00 and 23:59:59 hours inclusive. The system's current date and time can be retrieved using the Now, Date and Time functions in Visual Basic. The Now function retrieves the date and time, while Date function retrieves only date and Time function retrieves only the time. To display both the date and time together a message box is displayed use the statement given below. MsgBox "The current date and time of the system is" & Now Here & is used as a concatenation operator to concentrate the string and the Now function. Selective portions of the date and time value can be extracted using the below listed functions. Function Year ( ) Month ( ) Day ( ) WeekDay ( ) Hour ( ) Minute ( ) Second ( ) Extracted Portion Year (Now) Month (Now) Day (Now) WeekDay (Now) Hour (Now) Minute (Now) Second (Now)

The calculation and conversion functions related to date and time functions are listed below. Function Description

21

DateAdd ( ) DateDiff ( ) DatePart ( ) DateValue ( ) TimeValue ( ) DateSerial ( )

Returns a date to which a specific interval has been added Returns a Long data type value specifying the interval between the two values Returns an Integer containing the specified part of a given date Converts a string to a Date Converts a string to a time Returns a date for specified year, month and day

DateDiff Function The DateDiff function returns the intervals between two dates in terms of years, months or days. The syntax for this is given below. DateDiff (interval, date1, date2[, firstdayofweek[, firstweekofyear]]) Format Function The format function accepts a numeric value and converts it to a string in the format specified by the format argument. The syntax for this is given below. Format (expression[, format[, firstdayofweek[, firstweekofyear]]]) The Format function syntax has these parts: Part Expression format firstdayofweek firstweekofyear Description Required any valid expression Optional. A valid named or user-defined format expression. Optional. A contant that specifies the first day of the week. Optional. A contant that specifies the first week of the year

Working With Controls In Visual Basic 6 This lesson concentrates on Visual Basic controls and the ways of creating and implementing the. It also helps us to understand the concept of Control Arrays. Controls are used to recieve user input and display output and has its own set of properties, methods and events. Let us discuss few of these controls in this lesson.

22

Creating and Using Controls A control is an object that can be drawn on a Form object to enable or enhance user interaction with an application. Controls have properties that define aspects their appearance, such as position, size and colour, and aspects of their behavior, such as their response to the user input. They can respond to events initiated by the user or set off by the system. For instance, a code could be written in a CommandButton control's click event procedure that would load a file or display a result. In addition to properties and events, methods can also be used to manipulate controls from code. For instance, the move method can be used with some controls to change their location and size. Most of the controls provide choices to users that can be in the form of OptionButton or CheckBox controls, ListBox entries or ScrollBars to select a value. Let us discuss these controls by means of a few simple applications in the following lessons. Classification of Controls Visual Basic cojntrols are broadly classified as standard controls, ActiveX controls and insertable objects. Standard controls such as CommandButton, Label and Frame controls are contained inside .EXE file and are always included in the ToolBox which cannot be removed. ActiveX controls exist as separate files with either .VBX or .OCX extension. They include specialized controls such as; MSChart control The Communications control The Animation control The ListView control An ImageList control The Multimedia control The Internet Transfer control The WinSock control The TreeView control The SysInfo control The Picture Clip control Some of these objects support OLE Automation, which allow programming another application's object from within Visual Basic application. I would like to stress that knowing how and when to set the objects' properties is very important as it can help you to write a good program or you may fail to write a good program. So, I advice you to spend a lot of time playing with the objects' properties Here are some important points about setting up the properties You should set the Caption Property of a control clearly so that a user knows what to do with that command. For example, in the calculator program, all the captions of the command buttons such as +, - , MC, MR are commonly found in an ordinary calculator, a user should have no problem in manipulating the buttons. A lot of programmers like to use a meaningful name for the Name Property may be because it is easier for them to write and read the event procedure and easier to debug or modify the programs later. However, it is not a must to do that as long as you label your objects clearly and use comments in the program whenever you feel necessary One more important property is whether the control is enabled or not Finally, you must also considering making the control visible or invisible at runtime, or when should it become visible or invisible TabIndex property of Controls Visual Basic uses the TabIndex property to determine the control that would receive the focus next when a tab key is pressed. Every time a tab key is pressed, Visual Basic looks at the value of the TabIndex for the control that has focus and then it scans through the controls searching for the next highest TabIndex number. When there are no more controls with higher TabIndex value, Visual Basic starts all over again with 0 and looks for the first control with TabIndex of 0 or higher that can accept keyboard input. By default, Visual Basic assigns a tab order to control as we draw the controls on the Form, except for Menu, Timer, Data, Image, Line and Shape controls, which are not included in tab order. At run time, invisible or disabled controls also cannot receive the focus although a TabIndex value is given. Setting the TabIndex property of controls is compulsory in development environment. Using TextBox Control In Visual Basic 6

23

TextBox controls offer a natural way for users to enter a value in your program. For this reason, they tend to be the most frequently used controls in the majority of Windows applications. TextBox controls, which have a great many properties and events, are also among the most complex intrinsic controls. In this section, I guide you through the most useful properties of TextBox controls and show how to solve some of the problems that you're likely to encounter. Setting properties to a TextBox Text can be entered into the text box by assigning the necessary string to the text property of the control If the user needs to display multiple lines of text in a TextBox, set the MultiLine property to True To customize the scroll bar combination on a TextBox, set the ScrollBars property. Scroll bars will always appear on the TextBox when it's MultiLine property is set to True and its ScrollBars property is set to anything except None(0) If you set the MultilIne property to True, you can set the alignment using the Alignment property. The test is left-justified by default. If the MultiLine property is et to False, then setting the Alignment property has no effect. Run-Time Properties of a TextBox control The Text property is the one you'll reference most often in code, and conveniently it's the default property for the TextBox control. Three other frequently used properties are these: The SelStart property sets or returns the position of the blinking caret (the insertion point where the text you type appears). Note that the blinking cursor inside TextBox and other controls is named caret, to distinguish it from the cursor (which is implicitly the mouse cursor). When the caret is at the beginning of the contents of the TextBox control, SelStart returns 0; when it's at the end of the string typed by the user, SelStart returns the value Len(Text). You can modify the SelStart property to programmatically move the caret. The SelLength property returns the number of characters in the portion of text that has been highlighted by the user, or it returns 0 if there's no highlighted text. You can assign a nonzero value to this property to programmatically select text from code. Interestingly, you can assign to this property a value larger than the current text's length without raising a run-time error. The SelText property sets or returns the portion of the text that's currently selected, or it returns an empty string if no text is highlighted. Use it to directly retrieve the highlighted text without having to query Text, SelStart, and SelLength properties. What's even more interesting is that you can assign a new value to this property, thus replacing the current selection with your own. If no text is currently selected, your string is simply inserted at the current caret position. When you want to append text to a TextBox control, you should use the following code (instead of using the concatenation operator) to reduce flickering and improve performance: Text1.SelStart = Len(Text1.Text) Text1.SelText = StringToBeAdded One of the typical operations you could find yourself performing with these properties is selecting the entire contents of a TextBox control. You often do it when the caret enters the field so that the user can quickly override the existing value with a new one, or start editing it by pressing any arrow key: Private Sub Text1_GotFocus() Text1.SelStart = 0 ' A very high value always does the trick. Text1.SelLength = 9999 End Sub Always set the SelStart property first and then the SelLength or SelText properties. When you assign a new value to the SelStart property, the other two are automatically reset to 0 and an empty string respectively, thus overriding your previous settings. The selected text can be copied to the Clipboard by using SelText:

24

Clipboard.SelText text, [format] In the above syntax, text is the text that has to be placed into the Clipboard, and format has three possible values. 1. VbCFLink - conversation information 2. VbCFRTF - Rich Text Format 3. VbCFText - Text We can get text from the clipboard using the GetText() function this way: Clipboard.GetText ([format]) The following Figure summarizes the common TextBox control's properties and methods. Property/ Method Description

Properties Enabled Index specifies whether user can interact with this control or not Specifies the control array index If this control is set to True user can use it else if this control is set to false the control cannot be used Specifies the maximum number of characters to be input. Default value is set to 0 that means user can input any number of characters Using this we can set the shape of the mouse pointer when over a TextBox By setting this property to True user can have more than one line in the TextBox This is to specify mask character to be displayed in the TextBox This to set either the vertical scrollbars or horizontal scrollbars to make appear in the TextBox. User can also set it to both vertical and horizontal. This property is used with the Multiline property. Specifies the text to be displayed in the TextBox at runtime This is used to display what text is displayed or in the control By setting this user can make the Textbox control visible or invisible at runtime

Locked

MaxLength

MousePointer Multiline PasswordChar ScrollBars

Text ToolTipIndex Visible

Method SetFocus Transfers focus to the TextBox

25

Event procedures Change Click GotFocus LostFocus KeyDown KeyUp Action happens when the TextBox changes Action happens when the TextBox is clicked Action happens when the TextBox receives the active focus Action happens when the TextBox loses it focus Called when a key is pressed while the TextBox has the focus Called when a key is released while the TextBox has the focus

Trapping Keyboard Activity - Visual Basic 6 TextBox Control TextBox controls support KeyDown, KeyPress, and KeyUp standard events. One thing that you will often do is prevent the user from entering invalid keys. A typical example of where this safeguard is needed is a numeric field, for which you need to filter out all nondigit keys: Private Sub Text1_KeyPress(KeyAscii As Integer) Select Case KeyAscii Case Is < 32 ' Control keys are OK. Case 48 To 57 ' This is a digit. Case Else ' Reject any other key. KeyAscii = 0 End Select End Sub You should never reject keys whose ANSI code is less than 32, a group that includes important keys such as Backspace, Escape, Tab, and Enter. Also note that a few control keys will make your TextBox beep if it doesn't know what to do with themfor example, a single-line TextBox control doesn't know what to do with an Enter key. Don't assume that the KeyPress event will trap all control keys under all conditions. For example, the KeyPress event can process the Enter key only if there's no CommandButton control on the form whose Default property is set to True. If the form has a default push button, the effect of pressing the Enter key is clicking on that button. Similarly, no Escape key goes through this event if there's a Cancel button on the form. Finally, the Tab control key is trapped by a KeyPress event only if there isn't any other control on the form whose TabStop property is True. You can use the KeyDown event procedure to allow users to increase and decrease the current value using Up and Down arrow keys, as you see here: Private Sub Text1_KeyDown(KeyCode As Integer, Shift As Integer) Select Case KeyCode Case vbKeyUp Text1.Text = CDbl(Text1.Text) + 1 Case vbKeyDown Text1.Text = CDbl(Text1.Text) -1 End Select End Sub

26

There's a bug in the implementation of TextBox ready-only controls. When the Locked property is set to True, the Ctrl+C key combination doesn't correctly copy the selected text to the Clipboard, and you must manually implement this capability by writing code in the KeyPress event procedure Validation Routines For Numbers - Visual Basic 6 TextBox Control Although trapping invalid keys in the KeyPress or KeyDown event procedures seems a great idea at first, when you throw your application to inexperienced users you soon realize that there are many ways for them to enter invalid data. Depending on what you do with this data, your application can come to an abrupt end with a run-time error ormuch worseit can appear to work correctly while it delivers bogus results. What you really need is a bullet-proof method to trap invalid values. Before I offer you a decent solution to the problem, let me explain why you can't rely solely on trapping invalid keys for your validation chores. What if the user pastes an invalid value from the clipboard? Well, you might say, let's trap the Ctrl+V and Shift+Ins key combinations to prevent the user from doing that! Unfortunately, Visual Basic's TextBox controls offer a default edit menu that lets users perform any clipboard operation by simply right-clicking on them. Fortunately, there's a way around this problem: Instead of trapping a key before it gets to the TextBox control, you trap its effect in the Change event and reject it if it doesn't pass your test. But this makes the structure of the code a little more complex than you might anticipate: ' Form-level variables Dim saveText As String Dim saveSelStart As Long Private Sub Text1_GotFocus() ' Save values when the control gets the focus. saveText = Text1.Text saveSelStart = Text1.SelStart End Sub Private Sub Text1_Change() ' Avoid nested calls. Static nestedCall As Boolean If nestedCall Then Exit Sub ' Test the control's value here. If IsNumeric(Text1.Text) Then ' If value is OK, save values. saveText = Text1.Text saveSelStart = Text1.SelStart Else ' Prepare to handle a nested call. nestedCall = True Text1.Text = saveText nestedCall = False Text1.SelStart = saveSelStart End If End Sub Private Sub Text1_KeyUp(KeyCode As Integer, Shift As Integer) saveSelStart = Text1.SelStart End Sub Private Sub Text1_MouseDown(Button As Integer, _ Shift As Integer, X As Single, Y As Single) saveSelStart = Text1.SelStart End Sub Private Sub Text1_MouseMove(Button As Integer, _

27

Shift As Integer, X As Single, Y As Single) saveSelStart = Text1.SelStart End Sub If the control's value doesn't pass your tests in the Change event procedure, you must restore its previous valid value; this action recursively fires a Change event, and you must prepare yourself to neutralize this nested call. You might wonder why you also need to trap the KeyUp, MouseDown, and MouseMove events: The reason is that you always need to keep track of the last valid position for the insertion point because the end user could move it using arrow keys or the mouse. The preceding code snippet uses the IsNumeric function to trap invalid data. You should be aware that this function isn't robust enough for most real-world applications. For example, the IsNumeric function incorrectly considers these strings as valid numbers: 123,,,123 345$1234 ' What if it isn't a currency field? 2.4E10 ' What if I don't want to support scientific notation? To cope with this issue, I have prepared an alternative function, which you can modify for your particular purposes. (For instance, you can add support for a currency symbol or the comma as the decimal separator.) Note that this function always returns True when it's passed a null string, so you might need to perform additional tests if the user isn't allowed to leave the field blank: Function CheckNumeric(text As String, DecValue As Boolean) As Boolean Dim i As Integer For i = 1 To Len(text) Select Case Mid$(text, i, 1) Case "0" To "9" Case "-", "+" ' Minus/plus signs are only allowed as leading chars. If i > 1 Then Exit Function Case "." ' Exit if decimal values not allowed. If Not DecValue Then Exit Function ' Only one decimal separator is allowed. If InStr(text, ".") < i Then Exit Function Case Else ' Reject all other characters. Exit Function End Select Next CheckNumeric = True End Function If your TextBox controls are expected to contain other types of data, you might be tempted to reuse the same validation framework I showed you previouslyincluding all the code in the GotFocus, Change, KeyUp, MouseDown, and MouseMove event proceduresand replace only the call to IsNumeric with a call to your custom validation routine. Things aren't as simple as they appear at first, however. Say that you have a date field: Can you use the IsDate function to validate it from within the Change event? The answer is, of course, no. In fact, as you enter the first digit of your date value, IsDate returns False and the routine therefore prevents you from entering the remaining characters, and so preventing you from entering any value. This example explains why a key-level validation isn't always the best answer to your validation needs. For this reason, most Visual Basic programmers prefer to rely on field-level validation and test the values only when the user moves the input focus to another field in the form. I explain field-level validation in the next section. The CausesValidation Property And The Validate Event - Visual Basic 6 TextBox Control

28

Visual Basic 6 has finally come up with a solution for most of the validation issues that have afflicted Visual Basic developers for years. As you'll see in a moment, the Visual Basic 6 approach is simple and clean; it really astonishes me that it took six language versions to deliver such a lifesaver. The keys to the new validation features are the Validate event and the CausesValidation property. They work together as follows: When the input focus leaves a control, Visual Basic checks the CausesValidation property of the control that is about to receive the focus. If this property is True, Visual Basic fires the Validate event in the control that's about to lose the focus, thus giving the programmer a chance to validate its contents and, if necessary, cancel the focus shift. Let's try a practical example. Imagine that you have five controls on a form: a required field (a TextBox control, txtRequired, that can't contain an empty string), a numeric field, txtNumeric, that expects a value in the range 1 through 1000, and three push buttons: OK, Cancel, and Help. (See the figure below.) You don't want to perform validation if the user presses the Cancel or Help buttons, so you set their CausesValidation properties to False. The default value for this property is True, so you don't have to modify it for the other controls. Run the sample program on the companion CD, type something in the required TextBox, and then move to the second field. Because the second field's CausesValidation property is True, Visual Basic fires a Validate event in the first TextBox control: Private Sub txtRequired_Validate(Cancel As Boolean) ' Check that field is not empty. If txtRequired.Text = "" Then MsgBox "Please enter something here", vbExclamation Cancel = True End If End Sub If the Cancel parameter is set to True, Visual Basic cancels the user's action and takes the input focus back on the txtRequired control: No other GotFocus and LostFocus events are generated. On the other hand, if you typed something in the required field, the focus will now be on the second field (the numeric text box). Try clicking on the Help or Cancel buttons: No Validate event will fire this time because you set the CausesValidation property for each of these controls to False. Instead, click on the OK button to execute the Validate event of the numeric field, where you can check it for invalid characters and valid range.

A demonstration program that lets you experiment with the new Visual Basic Validate features Private Sub txtNumeric_Validate(Cancel As Boolean) If Not IsNumeric(txtNumeric.Text) Then Cancel = True ElseIf CDbl(txtNumeric.Text) < 1 Or CDbl(txtNumeric.Text) > 1000 Then Cancel = True End If If Cancel Then MsgBox "Please enter a number in range [1-1000]", vbExclamation End If End Sub

29

In some circumstances, you might want to programmatically validate the control that has the focus without waiting for the user to move the input focus. You can do it with the form's ValidateControls method, which forces the Validate event of the control that has the input focus. Typically, you do it when the user closes the form: Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) ' You can't close this form without validating the current field. If UnloadMode = vbFormControlMenu Then On Error Resume Next ValidateControls If Err = 380 Then ' The current field failed validation. Cancel = True End If End If End Sub Checking the UnloadMode parameter is important; otherwise, your application will mistakenly execute a ValidateControls method when the user clicks on the Cancel button. Note that ValidateControls returns an error 380 if Cancel was set in the Validate event procedure of the control that had the focus. Visual Basic 6's validation scheme has two flaws, though. If your form has a CommandButton whose Default property is set to True, pressing the Enter key while the input focus is on another control results in a click on the CommandButton control but doesn't fire a Validate event, even if the CausesValidation property of the CommandButton control is set to True. The only way to solve this problem is to invoke the ValidateControls method from within the default CommandButton control's Click event procedure. The second flaw is that the Validate event doesn't fire when you're moving the focus from a control whose CausesValidation property is False, even if the control that receives the focus has its CausesValidation property set to True. The new Visual Basic 6 validation mechanism is simple and can be implemented with little effort. But it isn't the magic answer to all your validation needs. In fact, this technique can only enforce field-level validation; it does nothing for recordlevel validation. In other words, it ensures that one particular field is correct, not that all fields in the form contain valid data. To see what I mean, run the demonstration program, enter a string in the first field, and press Alt+F4 to close the form. Your code won't raise an error, even if the second field doesn't contain a valid number! Fortunately, it doesn't take much to create a generic routine that forces each control on the form to validate itself: Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) ' You can't close this form without validating all the fields on it. If UnloadMode = vbFormControlMenu Then On Error Resume Next Dim ctrl As Control ' Give the focus to each control on the form, and then ' validate it. For Each ctrl In Controls Err.Clear ctrl.SetFocus If Err = 0 Then ' Don't validate controls that can't receive input focus. ValidateControls If Err = 380 Then ' Validation failed, refuse to close. Cancel = True: Exit Sub End If End If Next End If End Sub

30

The CausesValidation property and the Validate event are shared by all the intrinsic controls that are able to get the focus as well as by most external ActiveX controls, even those not specifically written for Visual Basic. This is possible because they are extender features, provided by the Visual Basic runtime to all the controls placed on a form's surface. One Visual Basic operator has great potential when it comes time to validate complex strings but is neglected by most Visual Basic developers. Let's say you have a product code that consists of two uppercase characters followed by exactly three digits. You might think that you need some complex string functions to validate such a string until you try the Like operator, as follows: If "AX123" Like "[A-Z][A-Z]###" Then Print "OK" Auto-Tabbing Fields And Formatting Text - Visual Basic 6 TextBox Control Auto-Tabbing Fields Users aren't usually delighted to spend all their time at the keyboard. Your job as a programmer is to make their jobs easier, and so you should strive to streamline their everyday work as much as possible. One way to apply this concept is to provide them with auto-tabbing fields, which are fields that automatically advance users to the next field in the Tab order as soon as they enter a valid value. Most often, auto-tabbing fields are those TextBox controls whose MaxLength property has been assigned a non-null value. Implementing such an auto-tabbing field in Visual Basic is straightforward: Private Sub Text1_Change() If Len(Text1.Text) = Text1.MaxLength Then SendKeys "{Tab}" End Sub The trick, as you see, is to have your program provide the Tab key on behalf of your user. In some cases, this simple approach doesn't workfor example, when you paste a long string into the field. You might want to write code that works around this and other shortcomings. Auto-tabbing is a nice feature but not vital to the application, so whether you write a workaround or not isn't a real problem in most cases. Formatting Text Many business applications let you enter data in one format and then display it in another. For example, numeric values can be formatted with thousand separators and a fixed number of decimal digits. Currency values might have a $ symbol (or whatever your national currency symbol is) automatically inserted. Phone numbers can be formatted with dashes to split into groups of digits. Credit-card numbers can be made more readable with embedded spaces. Dates can be shown in long-date format ("July 22, 2007"). And so on. The LostFocus event is an ideal occasion to format the contents of a TextBox control as soon as the input focus leaves it. In most cases, you can perform all your formatting chores using the Format function. For example, you can add thousand separators to a numeric value in the txtNumber control using this code: Private Sub txtNumber_LostFocus() On Error Resume Next txtNumber.Text = Format(CDbl(txtNumber.Text), _ "#,###,###,##0.######") End Sub When the field regains the focus, you'll want to get rid of those thousand separators. You can do it easily using the CDbl function: Private Sub txtNumber_GotFocus() ' On Error is necessary to account for empty fields. On Error Resume Next txtNumber.Text = CDbl(txtNumber.Text) End Sub

31

In some cases, however, formatting and unformatting a value isn't that simple. For example, you can format a Currency value to add parentheses around negative numbers, but there's no built-in Visual Basic function able to return a string formatted in that way to its original condition. Fear not, because nothing prevents you from creating your own formatting and unformatting routines. I have built two general-purpose routines for you to consider. The FilterString routine filters out all unwanted characters in a string: Function FilterString(Text As String, validChars As String) As String Dim i As Long, result As String For i = 1 To Len(Text) If InStr(validChars, Mid$(Text, i, 1)) Then result = result & Mid$(Text, i, 1) End If Next FilterString = result End Function FilterNumber builds on FilterString to strip down all formatting characters in a number and can also trim trailing decimal zeros: Function FilterNumber(Text As String, TrimZeros As Boolean) As String Dim decSep As String, i As Long, result As String ' Retrieve the decimal separator symbol. decSep = Format$(0.1, ".") ' Use FilterString for most of the work. result = FilterString(Text, decSep & "-0123456789") ' Do the following only if there is a decimal part and the ' user requested that nonsignificant digits be trimmed. If TrimZeros And InStr(Text, decSep) > 0 Then For i = Len(result) To 1 Step -1 Select Case Mid$(result, i, 1) Case decSep result = Left$(result, i - 1) Exit For Case "0" result = Left$(result, i - 1) Case Else Exit For End Select Next End If FilterNumber = result End Function The feature I like most in FilterNumber is that it's locale-independent. It works equally well on both sides of the Atlantic ocean (and on other continents, as well.) Instead of hard-coding the decimal separator character in the code, the routine determines it on the fly, using the Visual Basic for Applications (VBA) Format function. Start thinking internationally now, and you won't have a nervous breakdown when you have to localize your applications in German, French, and Japanese. The Format function lets you retrieve many locale-dependent characters and separators. Format$(0.1, ".") ' Decimal separator Format$(1, ",") ' Thousand separator Mid$(Format(#1/1/99#, "short date"), 2, 1) ' Date separator You can also determine whether the system uses dates in "mm/dd/yy" (U.S.) format or "dd/mm/yy" (European) format, using this code:

32

If Left$(Format$("12/31/1999", "short date"), 2) = 12 Then ' mm/dd/yy format Else ' dd/mm/yyyy format End If There's no direct way to determine the currency symbol, but you can derive it by analyzing the result of this function: Format$(0, "currency") ' Returns "$0.00" in US It isn't difficult to write a routine that internally uses the information I've just given you to extract the currency symbol as well as its default position (before or after the number) and the default number of decimal digits in currency values. Remember, in some countries the currency symbol is actually a string of two or more characters. To illustrate these concepts in action, I've built a simple demonstration program that shows how you can format numbers, currency values, dates, phone numbers, and credit-card numbers when exiting a field, and how you can remove that formatting from the result when the input focus reenters the TextBox control. Follwoing figure shows the formatted results. Private Sub txtNumber_GotFocus() ' Filter out nondigit chars and trailing zeros. On Error Resume Next txtNumber.Text = FilterNumber(txtNumber.Text, True) End Sub Private Sub txtNumber_LostFocus() ' Format as a number, grouping thousand digits. On Error Resume Next txtNumber.Text = Format(CDbl(txtNumber.Text), _ "#,###,###,##0.######") End Sub Private Sub txtCurrency_GotFocus() ' Filter out nondigit chars and trailing zeros. ' Restore standard text color. On Error Resume Next txtCurrency.Text = FilterNumber(txtCurrency.Text, True) txtCurrency.ForeColor = vbWindowText End Sub Private Sub txtCurrency_LostFocus() On Error Resume Next ' Show negative values as red text. If CDbl(txtCurrency.Text) < 0 Then txtCurrency.ForeColor = vbRed ' Format currency, but don't use parentheses for negative numbers. ' (FormatCurrency is a new VB6 string function.) txtCurrency.Text = FormatCurrency(txtCurrency.Text, , , vbFalse) End Sub Private Sub txtDate_GotFocus() ' Prepare to edit in short-date format. On Error Resume Next txtDate.Text = Format$(CDate(txtDate.Text), "short date") End Sub Private Sub txtDate_LostFocus() ' Convert to long-date format upon exit. On Error Resume Next

33

txtDate.Text = Format$(CDate(txtDate.Text), "d MMMM yyyy") End Sub Private Sub txtPhone_GotFocus() ' Trim embedded dashes. txtPhone.Text = FilterString(txtPhone.Text, "0123456789") End Sub Private Sub txtPhone_LostFocus() ' Add dashes if necessary. txtPhone.Text = FormatPhoneNumber(txtPhone.Text) End Sub Private Sub txtCreditCard_GotFocus() ' Trim embedded spaces. txtCreditCard.Text = FilterNumber(txtCreditCard.Text, True) End Sub Private Sub txtCreditCard_LostFocus() ' Add spaces if necessary. txtCreditCard.Text = FormatCreditCard(txtCreditCard.Text) End Sub Instead of inserting the code that formats phone numbers and credit-card numbers right in the LostFocus event procedures, I built two distinct routines, which can be more easily reused in other applications, as shown in the code below. Function FormatPhoneNumber(Text As String) As String Dim tmp As String If Text <> "" Then ' First get rid of all embedded dashes, if any. tmp = FilterString(Text, "0123456789") ' Then reinsert them in the correct position. If Len(tmp) <= 7 Then FormatPhoneNumber = Format$(tmp, "!@@@-@@@@") Else FormatPhoneNumber = Format$(tmp, "!@@@-@@@-@@@@") End If End If End Function Function FormatCreditCard(Text As String) As String Dim tmp As String If Text <> "" Then ' First get rid of all embedded spaces, if any. tmp = FilterNumber(Text, False) ' Then reinsert them in the correct position. FormatCreditCard = Format$(tmp, "!@@@@ @@@@ @@@@ @@@@") End If End Function Unfortunately, there isn't any way to create locale-independent routines that can format any phone number anywhere in the world. But by grouping all your formatting routines in one module, you can considerably speed up your work if and when it's time to convert your code for another locale. Multiline TextBox Controls - Visual Basic 6 TextBox Control You create multiline TextBox controls by setting the MultiLine property to True and the ScrollBars property to 2-Vertical or 3-Both. A vertical scroll bar causes the contents of the control to automatically wrap when a line is too long for the

34

control's width, so this setting is most useful when you're creating memo fields or simple word processor-like programs. If you have both a vertical and a horizontal scroll bar, the TextBox control behaves more like a programmer's editor, and longer lines simply extend beyond the right border. I've never found a decent use for the other settings of the ScrollBars property (0-None and 1-Horizontal) in a multiline TextBox control. Visual Basic ignores the ScrollBars property if MultiLine is False. Both these properties are read-only at run time, which means that you can't alternate between a regular and a multiline text box, or between a word processor-like multiline field (ScrollBars = 2-Vertical) and an editorlike field (ScrollBars = 3Both). To tell the whole truth, Visual Basic's support for multiline TextBox controls leaves much to be desired. You can do very little with such controls at run time, except to retrieve and set their Text properties. When you read the contents of a multiline TextBox control, it's up to you to determine where each line of text starts and ends. You do this with a loop that searches for carriage return (CR) and line feed (LF) pairs, or even more easily using the new Split string function: ' Print the lines of text in Text1, labeling them with their line numbers. Dim lines() As String, i As Integer lines() = Split(Text1.Text, vbCrLf) For i = 0 To UBound(lines) Print (i + 1) & ": " & lines(i) Next The support offered by Visual Basic for multiline TextBox controls ends here. The language doesn't offer any means for learning such vital information as at which point each line of text wraps, which are the first visible line and the first visible column, which line and column the caret is on, and so on. Moreover, you have no means of programmatically scrolling through a multiline text box. The solutions to these problems require Microsoft Windows API programming. In my opinion, however, Visual Basic should offer these features as built-in properties and methods. You should account for two minor issues when including one or more multiline TextBox controls on your forms. When you enter code in a word processor or an editor, you expect that the Enter key will add a newline character (more precisely, a CR-LF character pair) and that the Tab key will insert a tab character and move the caret accordingly. Visual Basic supports these keys, but because both of them have special meaning to Windows the support is limited: The Enter key adds a CR-LF pair only if there isn't a default push button on the form, and the Tab key inserts a tab character only if there aren't other controls on the form whose TabStop property is set to True. In many circumstances, these requirements can't be met, and some of your users will find your user interface annoying. If you can't avoid this problem, at least add a reminder to your users that they can add new lines using the Ctrl+Enter key combination and insert tab characters using the Ctrl+Tab key combination. Another possible approach is to set the TabStop property to False for all the controls in the form in the multiline TextBox's GotFocus event and to restore the original values in the LostFocus event procedure.

Label And Frame Controls In Visual Basic 6


Label and Frame controls have a few features in common, so it makes sense to explain them together. First they're mostly "decorative" controls that contribute to the user interface but are seldom used as programmable objects. In other words, you often place them on the form and arrange their properties as your user interface needs dictate, but you rarely write code to serve their events, generally, or manipulate their properties at run time.

Label Controls
Most people use Label controls to provide a descriptive caption and possibly an associated hot key for other controls, such as TextBox, ListBox, and ComboBox, that don't expose the Caption property. In most cases, you just place a Label control where you need it, set its Caption property to a suitable string (embedding an ampersand character in front of the hot key you want to assign), and you're done. Caption is the default property for Label controls. Be careful to set the Label's TabIndex property so that it's 1 minus the TabIndex property of the companion control. Other useful properties are BorderStyle(if you want the Label control to appear inside a 3D border) and Alignment (if you want to align the caption to the right or center it on the control). In most cases, the alignment depends on how the Label control relates to its companion control: for example, if the Label control is placed to the left of its companion field, you

35

might want to set its Alignment property to 1-Right Justify. The value 2-Center is especially useful for stand-alone Label controls.

Different settings for the Alignment property of Label controls. You can insert a literal & character in a Label control's Caption property by doubling it. For example, to see Research & Development you have to type &Research && Development. Note that if you have multiple but isolated &s, the one that selects the hot key is the last one and all others are ignored. This tip applies to all the controls that expose a Caption property. (The & has no special meaning in forms' Caption properties, however.) If the caption string is a long one, you might want to set the Label's WordWrap property to True so that it will extend for multiple lines instead of being truncated by the right border of the control. Alternatively, you might decide to set the AutoSize property to True and let the control automatically resize itself to accommodate longer caption strings. You sometimes need to modify the default value of a Label's BackStyle property. Label controls usually cover what's already on the form's surface (other lightweight controls, output from graphic methods, and so on) because their background is considered to be opaque. If you want to show a character string somewhere on the form but at the same time you don't want to obscure underlying objects, set the BackStyle property to 0-Transparent. If you're using the Label control to display data read from elsewherefor example, a database field or a text fileyou should set its UseMnemonics property to False. In this case, & characters have no special meaning to the control, and so you indirectly turn off the control's hot key capability. I mention this property because in older versions of Visual Basic, you had to manually double each & character to make the ampersand appear in text. I don't think all developers are aware that you can now treat ampersands like regular characters. As I said before, you don't usually write code in Label control event procedures. This control exposes only a subset of the events supported by other controls. For example, because Label controls can never get the input focus, they don't support GotFocus, LostFocus, or any keyboard-related events. In practice, you can take advantage only of their mouse events: Click, DblClick, MouseDown, MouseMove, and MouseUp. If you're using a Label control to display data read from a database, you might sometimes find it useful to write code in its Change event. A Label control doesn't expose a specific event that tells programmers when users press its hot keys. You can do some interesting tricks with Label controls. For example, you can use them to provide rectangular hot spots for images loaded onto the form. To create that context-sensitive ToolTip, I loaded the image on the form using the form's Picture property and then I placed a Label control over the Microsoft BackOffice logo, setting its Caption property to an empty string and the BackStyle property to 0-Transparent. These properties make the Label invisible, but it correctly shows its ToolTip when necessary. And because it still receives all mouse events, you can use its Click event to react to users' actions.

Frame Controls
Frame controls are similar to Label controls in that they can serve as captions for those controls that don't have their own. Moreover, Frame controls can also (and often do) behave as containers and host other controls. In most cases, you only

36

need to drop a Frame control on a form and set its Caption property. If you want to create a borderless frame, you can set its BorderStyle property to 0-None. Controls that are contained in the Frame control are said to be child controls. Moving a control at design time over a Frame controlor over any other container, for that matterdoesn't automatically make that control a child of the Frame control. After you create a Frame control, you can create a child control by selecting the child control's icon in the Toolbox and drawing a new instance inside the Frame's border. Alternatively, to make an existing control a child of a Frame control, you must select the control, press Ctrl+X to cut it to the Clipboard, select the Frame control, and press Ctrl+V to paste the control inside the Frame. If you don't follow this procedure and you simply move the control over the Frame, the two controls remain completely independent of each other, even if the other control appears in front of the Frame control. Frame controls, like all container controls, have two interesting features. If you move a Frame control, all the child controls go with it. If you make a container control disabled or invisible, all its child controls also become disabled or invisible. You can exploit these features to quickly change the state of a group of related controls.

VB6 CommandButton And OptionButton Controls


When compared to TextBox controls, these controls are really simple. Not only do they expose relatively few properties, they also support a limited number of events, and you don't usually write much code to manage them.

CommandButton Controls in VB6


Using CommandButton controls is trivial. In most cases, you just draw the control on the form's surface, set its Caption property to a suitable string (adding an & character to associate a hot key with the control if you so choose), and you're finished, at least with user-interface issues. To make the button functional, you write code in its Click event procedure, as in this fragment: Private Sub Command1_Click() ' Save data, then unload the current form. Call SaveDataToDisk Unload Me End Sub You can use two other properties at design time to modify the behavior of a CommandButton control. You can set the Default property to True if it's the default push button for the form (the button that receives a click when the user presses the Enter keyusually the OK or Save button). Similarly, you can set the Cancel property to True if you want to associate the button with the Escape key. The only relevant CommandButton's run-time property is Value, which sets or returns the state of the control (True if pressed, False otherwise). Value is also the default property for this type of control. In most cases, you don't need to query this property because if you're inside a button's Click event you can be sure that the button is being activated. The Value property is useful only for programmatically clicking a button: This fires the button's Click event. Command1.Value = True The CommandButton control supports the usual set of keyboard and mouse events (KeyDown, KeyPress, KeyUp, MouseDown, MouseMove, MouseUp, but not the DblClick event) and also the GotFocus and LostFocus events, but you'll rarely have to write code in the corresponding event procedures.

Properties of a CommandButton control


To display text on a CommandButton control, set its Caption property. An event can be activated by clicking on the CommandButton.

37

To set the background colour of the CommandButton, select a colour in the BackColor property. To set the text colour set the Forecolor property. Font for the CommandButton control can be selected using the Font property. To enable or disable the buttons set the Enabled property to True or False To make visible or invisible the buttons at run time, set the Visible property to True or False. Tooltips can be added to a button by setting a text to the Tooltip property of the CommandButton. A button click event is handled whenever a command button is clicked. To add a click event handler, double click the button at design time, which adds a subroutine like the one given below. Private Sub Command1_Click( ) .................. End Sub

OptionButton Controls in VB6


OptionButton controls are also known as radio buttons because of their shape. You always use OptionButton controls in a group of two or more because their purpose is to offer a number of mutually exclusive choices. Anytime you click on a button in the group, it switches to a selected state and all the other controls in the group become unselected. Preliminary operations for an OptionButton control are similar to those already described for CheckBox controls. You set an OptionButton control's Caption property to a meaningful string, and if you want you can change its Alignment property to make the control right aligned. If the control is the one in its group that's in the selected state, you also set its Valueproperty to True. (The OptionButton's Value property is a Boolean value because only two states are possible.) Value is the default property for this control. At run time, you typically query the control's Value property to learn which button in its group has been selected. Let's say you have three OptionButton controls, named optWeekly, optMonthly, and optYearly. You can test which one has been selected by the user as follows: If optWeekly.Value Then ' User prefers weekly frequency. ElseIf optMonthly.Value Then ' User prefers monthly frequency. ElseIf optYearly.Value Then ' User prefers yearly frequency. End If Strictly speaking, you can avoid the test for the last OptionButton control in its group because all choices are supposed to be mutually exclusive. But the approach I just showed you increases the code's readability. A group of OptionButton controls is often hosted in a Frame control. This is necessary when there are other groups of OptionButton controls on the form. As far as Visual Basic is concerned, all the OptionButton controls on a form's surface belong to the same group of mutually exclusive selections, even if the controls are placed at the opposite corners of the window. The only way to tell Visual Basic which controls belong to which group is by gathering them inside a Frame control. Actually, you can group your controls within any control that can work as a containerPictureBox, for example but Frame controls are often the most reasonable choice. Example Open a new Standard EXE project and the save the Form as Option.frm and save the project as Option.vbp. Design the Form as per the following specifications table. Object Property Settings

38

Label

Caption Name

Enter a Number Label1 (empty) Text1 &Close Command1 &Octal optOct &Hexadecimal optHex &Decimal optDec

TextBox

Text Name

CommandButton

Caption Name

OptionButton

Caption Name

OptionButton

Caption Name

OptionButton

Caption Name

The application responds to the following events The change event of the TextBox reads the value and stores it in a form-level numeric variable. The click event of optOct button returns curretval in octal. The click event of the optHex button curerntval in hexadecimal The click event of the optDec button returns the decimal equivalent of the value held currentval.

The following code is entered in the general declarations section of the Form. Dim currentval as variant The variable is initialized to 0 by default. The change event procedure checks to ascertain the number system (Octal, Hexadecimal) that is in effect and then reads in the number. Private Sub Text1_Change() If optOct.Value = True Then currentval = Val ("&O" & LTrim (Text1.Text) & "&") Elseif optDec.value = True Then currentval = Val (LTrim (Text1.Text) & "&") Else currentval = Val ("&H" & LTrim (Text1.Text) & "&") End if End Sub The Val function is used to translate string to a number and can recognize Octal and Hexadecimal strings. The LTrim function trims the leading blanks in the text. The following code is entered in the click events of the OptionButton controls. Private Sub optOct_Click() Text1.Text = Oct(currentval) End Sub Private Sub optHex_Click() Text1.Text = Hex(currentval) End Sub

39

Private Sub optDec_Click() Text1.Text = Format(currentval) End Sub The follwoing code is entered in the click event of teh Close button. Private Sub cmdClose_Click() Unlod Me End Sub The Application is run by pressing F5 or clicking on the Run icon in the tool bar. By pressing the Exit button the program is terminated.

PictureBox And Image Controls In Visual Basic 6


Both PictureBox and Image controls let you display an image, so let's compare them and see when it makes sense to choose one or the other.

The PictureBox Control


PictureBox controls are among the most powerful and complex items in the Visual Basic Toolbox window. In a sense, these controls are more similar to forms than to other controls. For example, PictureBox controls support all the properties related to graphic output, including AutoRedraw, ClipControls, HasDC, FontTransparent, CurrentX, CurrentY, and all the Drawxxxx, Fillxxxx, and Scalexxxx properties. PictureBox controls also support all graphic methods, such as Cls, PSet, Point, Line, and Circle and conversion methods, such as ScaleX, ScaleY, TextWidth, and TextHeight. In other words, all the techniques that I described for forms can also be used for PictureBox controls (and therefore won't be covered again in this section).

Loading images
Once you place a PictureBox on a form, you might want to load an image in it, which you do by setting the Picture property in the Properties window. You can load images in many different graphic formats, including bitmaps (BMP), device independent bitmaps (DIB), metafiles (WMF), enhanced metafiles (EMF), GIF and JPEG compressed files, and icons (ICO and CUR). You can decide whether a control should display a border, resetting the BorderStyle to 0-None if necessary. Another property that comes handy in this phase is AutoSize: Set it to True and let the control automatically resize itself to fit the assigned image. You might want to set the Align property of a PictureBox control to something other than the 0-None value. By doing that, you attach the control to one of the four form borders and have Visual Basic automatically move and resize the PictureBox control when the form is resized. PictureBox controls expose a Resize event, so you can trap it if you need to move and resize its child controls too. You can do more interesting things at run time. To begin with, you can programmatically load any image in the control using the LoadPicture function: Picture1.Picture = LoadPicture("c:\windows\setup.bmp") and you can clear the current image using either one of the following statements: ' These are equivalent. Picture1.Picture = LoadPicture("") Set Picture1.Picture = Nothing

40

The LoadPicture function has been extended in Visual Basic 6 to support icon files containing multiple icons. The new syntax is the following: LoadPicture(filename, [size], [colordepth], [x], [y]) where values in square brackets are optional. If filename is an icon file, you can select a particular icon using the size or colordepth arguments. Valid sizes are 0-vbLPSmall, 1-vbLPLarge (system icons whose sizes depend on the video driver), 2-vbLPSmallShell, 3-vbLPLargeShell (shell icons whose dimensions are affected by the Caption Button property as set in the Appearance tab in the screen's Properties dialog box), and 4-vbLPCustom (size is determined by x and y). Valid color depths are 0-vbLPDefault (the icon in the file that best matches current screen settings), 1-vbLPMonochrome, 2vbLPVGAColor (16 colors), and 3-vbLPColor (256 colors). You can copy an image from one PictureBox control to another by assigning the target control's Picture property: Picture2.Picture = Picture1.Picture

The PaintPicture method


PictureBox controls are equipped with a very powerful method that enables the programmer to perform a wide variety of graphic effects, including zooming, scrolling, panning, tiling, flipping, and many fading effects: This is the PaintPicture method. (This method is also exposed by form objects, but it's most often used with PictureBox controls.) In a nutshell, this method performs a pixel-by-pixel copy from a source control to a destination control. The complete syntax of this method is complex and rather confusing: DestPictBox.PaintPicture SrcPictBox.Picture, destX, destY, [destWidth], _ [destHeight], [srcX], [srcY2], [srcWidth], [srcHeight], [Opcode]) The only required arguments are the source PictureBox control's Picture property and the coordinates inside the destination control where the image must be copied. The destX / destY arguments are expressed in the ScaleMode of the destination control; by varying them, you can make the image appear exactly where you want. For example, if the source PictureBox control contains a bitmap 3000 twips wide and 2000 twips tall, you can center this image on the destination control with this command: picDest.PaintPicture picSource.Picture, (picDest.ScaleWidth - 3000) / 2, _ (picDest.ScaleHeight - 2000) / 2 In general, Visual Basic doesn't provide a way to determine the size of a bitmap loaded into a PictureBox control. But you can derive this information if you set the control's AutoSize property to True and then read the control's ScaleWidth and ScaleHeight properties. If you don't want to resize a visible control just to learn the dimensions of a bitmap, you can load it into an invisible control, or you can use this trick, based on the fact that the Picture property returns an StdPicture object, which in turn exposes the Height and Width properties: ' StdPicture's Width and Height properties are expressed in ' Himetric units. With Picture1 width = CInt(.ScaleX(.Picture.Width, vbHimetric, vbPixels)) height = CInt(.ScaleY(.Picture.Height, vbHimetric, _ vbPixels)) End With By the way, in all subsequent code examples I assume that the source PictureBox control's ScaleWidth and ScaleHeight properties match the actual bitmap's size. By default, the PaintPicture method copies the entire source bitmap. But you can copy just a portion of it, passing a value for srcWidth and srcHeight:

41

' Copy the upper left portion of the source image. picDest.PaintPicture picSource.Picture, 0, 0, , , , , _ picSource.ScaleWidth / 2, picSource.ScaleHeight / 2 If you're copying just a portion of the source image, you probably want to pass a specific value for the srcX and srcY values as well, which correspond to the coordinates of the top-left corner of the area that will be copied from the source control: ' Copy the bottom-right portion of the source image ' in the corresponding corner in the destination. wi = picSource.ScaleWidth / 2 he = picSource.ScaleHeight / 2 picDest.PaintPicture picSource.Picture, wi, he, , , wi, he, wi, he You can use this method to tile a target PictureBox control (or form) with multiple copies of an image stored in another control: ' Start with the leftmost column. x=0 Do While x < picDest.ScaleWidth y=0 ' For each column, start at the top and work downward. Do While y < picDest.ScaleHeight picDest.PaintPicture picSource.Picture, x, y, , , 0, 0 ' Next row y = y + picSource.ScaleHeight Loop ' Next column x = x + picSource.ScaleWidth Loop Another great feature of the PaintPicture method lets you resize the image while you transfer it, and you can even specify different zoom-in and zoom-out factors for the x- and y-axes independently. You just have to pass a value to the destWidth and destHeight arguments: If these values are greater than the source image's corresponding dimensions, you achieve a zoom-in effect, and if they are less you get a zoom-out effect. For example, see how you can double the size of the original image: picDest.PaintPicture picSource.Picture, 0, 0, _ picSource.ScaleWidth * 2, picSource.ScaleHeight * 2 As a special case of the syntax of the PaintPicture method, the source image can even be flipped along its x-axis, y-axis, or both by passing negative values for these arguments: ' Flip horizontally. picDest.PaintPicture picSource.Picture, _ picSource.ScaleWidth, 0, -picSource.ScaleWidth ' Flip vertically. picDest.PaintPicture picSource.Picture, 0, _ picSource.ScaleHeight, , -picSource.ScaleHeight ' Flip the image on both axes. picDest.PaintPicture picSource.Picture, picSource.ScaleWidth, _ picSource.ScaleHeight, -picSource.ScaleWidth, -picSource.ScaleHeight As you might expect, you can combine all these effects together, magnifying, reducing, or flipping just a portion of the source image, and have the result appear in any point of the destination PictureBox control (or form). You should find no problem in reusing all those routines in your own applications.

42

As if all these capabilities weren't enough, we haven't covered the last argument of the PaintPicture method yet. The opcode argument lets you specify which kind of Boolean operation must be performed on pixel bits as they're transferred from the source image to the destination. The values you can pass to this argument are the same that you assign to the DrawMode property. The default value is 13-vbCopyPen, which simply copies the source pixels in the destination control. By playing with the other settings, you can achieve many interesting graphical effects, including simple animations.

The Image Control


Image controls are far less complex than PictureBox controls. They don't support graphical methods or the AutoRedraw and the ClipControls properties, and they can't work as containers, just to hint at their biggest limitations. Nevertheless, you should always strive to use Image controls instead of PictureBox controls because they load faster and consume less memory and system resources. Remember that Image controls are windowless objects that are actually managed by Visual Basic without creating a Windows object. Image controls can load bitmaps and JPEG and GIF images. When you're working with an Image control, you typically load a bitmap into its Picture property either at design time or at run time using the LoadPicture function. Image controls don't expose the AutoSize property because by default they resize to display the contained image (as it happens with PictureBox controls set at AutoSize = True). On the other hand, Image controls support a Stretch property that, if True, resizes the image (distorting it if necessary) to fit the control. In a sense, the Stretch property somewhat remedies the lack of the PaintPicture method for this control. In fact, you can zoom in to or reduce an image by loading it in an Image control and then setting its Stretch property to True to change its width and height: ' Load a bitmap. Image1.Stretch = False Image1.Picture = LoadPicture("c:\windows\setup.bmp") ' Reduce it by a factor of two. Image1.Stretch = True Image1.Move 0, 0, Image1.Width / 2, Image1.Width / 2 Image controls support all the usual mouse events. For this reason, many Visual Basic developers have used Image controls to simulate graphical buttons and toolbars. Now that Visual Basic natively supports these controls, you'd probably better use Image controls only for what they were originally intended.

The Timer, Line, Shape And OLE Controls In Visual Basic 6


A Timer control is invisible at run time, and its purpose is to send a periodic pulse to the current application. You can trap this pulse by writing code in the Timer's Timer event procedure and take advantage of it to execute a task in the background or to monitor a user's actions. This control exposes only two meaningful properties: Interval and Enabled. Interval stands for the number of milliseconds between subsequent pulses (Timer events), while Enabled lets you activate or deactivate events. When you place the Timer control on a form, its Interval is 0, which means no events. Therefore, remember to set this property to a suitable value in the Properties window or in the Form_Load event procedure: Private Sub Form_Load() Timer1.Interval = 500 ' Fire two Timer events per second. End Sub Timer controls let you write interesting programs with just a few lines of code. The typical (and abused) example is a digital clock. Just to make things a bit more compelling, I added flashing colons: Private Sub Timer1_Timer() Dim strTime As String strTime = Time$ If Mid$(lblClock.Caption, 3, 1) = ":" Then Mid$(strTime, 3, 1)= " " Mid$(strTime, 6, 1) = " " End If

43

lblClock.Caption = strTime End Sub You must be careful not to write a lot of code in the Timer event procedure because this code will be executed at every pulse and therefore can easily degrade your application's performance. Just as important, never execute a DoEvents statement inside a Timer event procedure because you might cause the procedure to be reentered, especially if the Interval property is set to a small value and there's a lot of code inside the procedure. Timer controls are often useful for updating status information on a regular basis. For example, you might want to display on a status bar a short description of the control that currently has the input focus. You can achieve that by writing some code in the GotFocus event for all the controls on the form, but when you have dozens of controls this will require a lot of code (and time). Instead, at design time load a short description for each control in its Tag property, and then place a Timer control on the form with an Interval setting of 500. This isn't a time-critical task, so you can use an even larger value. Finally add two lines of code to the control's Timer event: Private Sub Timer1_Timer() On Error Resume Next lblStatusBar.Caption = ActiveControl.Tag End Sub

The Line Control


The Line control is a decorative control whose only purpose is let you draw one or more straight lines at design time, instead of displaying them using a Line graphical method at run time. This control exposes a few properties whose meaning should sound familiar to you by now: BorderColor (the color of the line), BorderStyle (the same as a form's DrawStyle property), BorderWidth (the same as a form's DrawWidth property), and DrawMode. While the Line control is handy, remember that using a Line method at run time is usually better in terms of performance.

The Shape Control


In a sense, the Shape control is an extension of the Line control. It can display six basic shapes: Rectangle, Square, Oval, Circle, Rounded Rectangle, and Rounded Square. It supports all the Line control's properties and a few more: BorderStyle (0-Transparent, 1-Solid), FillColor, and FillStyle (the same as a form's properties with the same names). The same performance considerations I pointed out for the Line control apply to the Shape control.

The OLE Control


When OLE first made its appearance, the concept of Object Linking and Embedding seemed to most developers nothing short of magic. The ability to embed a Microsoft Word Document or a Microsoft Excel worksheet within another Windows application seemed an exciting one, and Microsoft promptly released the OLE controlthen called the OLE Container controlto help Visual Basic support this capability. In the long run, however, the Embedding term in OLE has lost much of its appeal and importance, and nowadays programmers are more concerned and thrilled about Automation, a subset of OLE that lets them control other Windows applications from the outside, manipulating their object hierarchies through OLE. For this reason, I won't describe the OLE control: It's a rather complex object, and a thorough description of its many properties, methods, and events (and quirks) would take too much space.

Using ListBox And ComboBox Controls In Visual Basic 6


ListBox and ComboBox controls present a set of choices that are displayed vertically in a column. If the number of items exceed the value that be displayed, scroll bars will automatically appear on the control. These scroll bars can be scrolled up and down or left to right through the list.

44

The following Fig lists some of the common ComboBox properties and methods. Property/Method Properties Enabled By setting this property to True or False user can decide whether user can interact with this control or not Specifies the Control array index String array. Contains the strings displayed in the drop-down list. Starting array index is 0. Integer. Contains the number of drop-down list items Integer. Contains the index of the selected ComboBox item. If an item is not selected, ListIndex is -1 Boolean. Specifies whether user can type or not in the ComboBox Integer. Specifies the shape of the mouse pointer when over the area of the ComboBox Integer. Index of the last item added to the ComboBox. If the ComboBox does not contain any items , NewIndex is -1 Boolean. Specifies whether the ComboBox's items are sorted or not. Integer. Specifies the style of the ComboBox's appearance Boolean. Specifies whether ComboBox receives the focus or not. String. Specifies the selected item in the ComboBox String. Specifies what text is displayed as the ComboBox's tool tip Boolean. Specifies whether ComboBox is visible or not at run time Description

Index

List

ListCount

ListIndex

Locked

MousePointer

NewIndex

Sorted Style TabStop Text ToolTipIndex Visible Methods AddItem Clear RemoveItem SetFocus Event Procedures

Add an item to the ComboBox Removes all items from the ComboBox Removes the specified item from the ComboBox Transfers focus to the ComboBox

45

Change DropDown GotFocus LostFocus

Called when text in ComboBox is changed Called when the ComboBox drop-down list is displayed Called when ComboBox receives the focus Called when ComboBox loses it focus

Adding items to a List


It is possible to populate the list at design time or run time Design Time : To add items to a list at design time, click on List property in the property box and then add the items. Press CTRL+ENTER after adding each item as shown below.

Run Time : The AddItem method is used to add items to a list at run time. The AddItem method uses the following syntax. Object.AddItemitem, Index The item argument is a string that represents the text to add to the list The index argument is an integer that indicates where in the list to add the new item. Not giving the index is not a problem, because by default the index is assigned. Following is an example to add item to a combo box. The code is typed in the Form_Load event Private Sub Form_Load() Combo1.AddItem 1 Combo1.AddItem 2 Combo1.AddItem 3 Combo1.AddItem 4 Combo1.AddItem 5 Combo1.AddItem 6 End Sub

Removing Items from a List


The RemoveItem method is used to remove an item from a list. The syntax for this is given below. Object.RemoveItem index

46

The following code verifies that an item is selected in the list and then removes the selected item from the list. Private Sub cmdRemove_Click() If List1.ListIndex > -1 Then List1.RemoveItem List1. ListIndex End If End Sub

Sorting the List


The Sorted property is set to True to enable a list to appear in alphanumeric order and False to display the list items in the order which they are added to the list.

Using the ComboBox


A ComboBox combines the features of a TextBox and a ListBox. This enables the user to select either by typing text into the ComboBox or by selecting an item from the list. There are three types of ComboBox styles that are represented as shown below.

Dropdown combo

Simple combo

Dropdown list

Dropdown Combo (style 0) Simple Combo (style 1) Dropdown List (style 2)

The Simple Combo box displays an edit area with an attached list box always visible immediately below the edit area. A simple combo box displays the contents of its list all the time. The user can select an item from the list or type an item in the edit box portion of the combo box. A scroll bar is displayed beside the list if there are too many items to be displayed in the list box area. The Dropdown Combo box first appears as only an edit area with a down arrow button at the right. The list portion stays hidden until the user clicks the down-arrow button to drop down the list portion. The user can either select a value from the list or type a value in the edit area. The Dropdown list combo box turns the combo box into a Dropdown list box. At run time , the control looks like the Dropdown combo box. The user could click the down arrow to view the list. The difference between Dropdown combo & Dropdown list combo is that the edit area in the Dropdown list combo is disabled. The user can only select an item and cannot type anything in the edit area. Anyway this area displays the selected item. Example This example is to Add , Remove, Clear the list of items and finally close the application. Open a new Standard EXE project is opened an named the Form as Listbox.frm and save the project as Listbox.vbp Design the application as shown below.

47

Object

Property Caption Name ListBox

Settings

Form

frmListBox (empty) txtName Enter a name lblName lstName Amount Entered lblAmount (empty) lblDisplay 1 Fixed Single Add cmdAdd Remove cmdRemove Clear cmdClear Exit cmdExit

TextBox

Text Name

Label

Caption Name

ListBox

Name Caption Name Caption

Label

Label

Name Border Style

CommandButton

Caption Name

CommandButton

Caption Name

CommandButton

Caption Name

CommandButton

Caption Name

The following event procedures are entered for the TextBox and CommandButton controls. Private Sub txtName_Change() If (Len(txtName.Text) > 0) Then 'Enabling the Add button

48

'if atleast one character 'is entered cmdAdd.Enabled = True End If End Sub Private Sub cmdAdd_Click() lstName.AddItem txtName.Text 'Add the entered the characters to the list box txtName.Text = "" 'Clearing the text box txtName.SetFocus 'Get the focus back to the 'text box lblDisplay.Caption = lstName.ListCount 'Display the number of items in the list box cmdAdd.Enabled = False ' Disabling the Add button End Sub The click event of the Add button adds the text to the list box that was typed in the Text box. Then the text box is cleared and the focus is got to the text box. The number of entered values will is increased according to the number of items added to the listbox. Private Sub cmdClear_Click() lstName.Clear lblDisplay.Caption = lstName.ListCount End Sub Private Sub cmdExit_Click() Unload Me End Sub Private Sub cmdRemove_Click() Dim remove As Integer remove = lstName.ListIndex 'Getting the index If remove >= 0 Then 'make sure an item is selected 'in the list box lstName.RemoveItem remove 'Remove item from the list box lblDisplay.Caption = lstName.ListCount 'Display the number of items 'in the listbox End If End Sub Remove button removes the selected item from the list as soon as you pressed the Remove button. The number of items is decreased in the listbox and the value is displayed in the label. The code for the clear button clears the listbox when you press it. And the number of items shown in the label becomes 0.

49

VB ScrollBar - Using ScrollBar Control


The ScrollBar is a commonly used control, which enables the user to select a value by positioning it at the desired location. It represents a set of values. The Min and Max property represents the minimum and maximum value. The value property of the ScrollBar represents its current value, that may be any integer between minimum and maximum values assigned. The HScrollBar and the VScrollBar controls are perfectly identical, apart from their different orientation. After you place an instance of such a control on a form, you have to worry about only a few properties: Min and Max represent the valid range of values, SmallChange is the variation in value you get when clicking on the scroll bar's arrows, and LargeChange is the variation you get when you click on either side of the scroll bar indicator. The default initial value for those two properties is 1, but you'll probably have to change LargeChange to a higher value. For example, if you have a scroll bar that lets you browse a portion of text, SmallChange should be 1 (you scroll one line at a time) and LargeChange should be set to match the number of visible text lines in the window. The most important run-time property is Value, which always returns the relative position of the indicator on the scroll bar. By default, the Min value corresponds to the leftmost or upper end of the control: ' Move the indicator near the top (or left) arrow. VScroll1.Value = VScroll1.Min ' Move the indicator near the bottom (or right) arrow. VScroll1.Value = VScroll1.Max While this setting is almost always OK for horizontal scroll bars, you might sometimes need to reverse the behavior of vertical scroll bars so that the zero is near the bottom of your form. This arrangement is often desirable if you want to use a vertical scroll bar as a sort of slider. You obtain this behavior by simply inverting the values in the Min and Max properties. (In other words, it's perfectly legal for Min to be greater than Max.) There are two key events for scrollbar controls: the Change event fires when you click on the scroll bar arrows or when you drag the indicator; the Scroll event fires while you drag the indicator. The reason for these two distinct possibilities is mostly historical. First versions of Visual Basic supported only the Change event, and when developers realized that it wasn't possible to have continuous feedback when users dragged the indicator, Microsoft engineers added a new event instead of extending the Change event. In this way, old applications could be recompiled without unexpected changes in their behavior. At any rate, this means that you must often trap two distinct events: ' Show the current scroll bar's value. Private VScroll1_Change() Label1.Caption = VScroll1.Value End Sub Private VScroll1_Scroll() Label1.Caption = VScroll1.Value End Sub The example shown in the following figure uses three VScrollBar controls as sliders to control the individual RGB (red, green, blue) components of a color. The three scroll bars have their Min property set to 255 and their Max property set to 0, while their SmallChange is 1 and LargeChange is 16. This example is also a moderately useful program in itself because you can select a color and then copy its numeric value to the clipboard and paste it in your application's code as a decimal value, a hexadecimal value, or an RGB function.

50

Use scrollbar controls to visually create colors. Scrollbar controls can receive the input focus, and in fact they support both the TabIndex and TabStop properties. If you don't want the user to accidentally move the input focus on a scrollbar control when he or she presses the Tab key, you must explicitly set its TabStop property to False. When a scrollbar control has the focus, you can move the indicator using the Left, Right, Up, Down, PgUp, PgDn, Home, and End keys. For example, you can take advantage of this behavior to create a read-only TextBox control with a numeric value that can be edited only through a tiny companion scroll bar. This scroll bar appears to the user as a sort of spin button, as you can see in the figure below. To make the trick work, you need to write just a few lines of code: Private Sub Text1_GotFocus() ' Pass the focus to the scroll bar. VScroll1.SetFocus End Sub Private Sub VScroll1_Change() ' Scroll bar controls the text box value. Text1.Text = VScroll1.Value End Sub

You don't need external ActiveX controls to create functional spin buttons Scrollbar controls are even more useful for building scrolling forms, like the one displayed in Figure 3-15. To be certain, scrolling forms aren't the most ergonomic type of user interface you can offer to your customers: If you have that many fields in a form, you should consider using a Tab control, child forms, or some other custom interface. Sometimes, however, you badly need scrollable forms, and in this situation you are on your own because Visual Basic forms don't support scrolling. Fortunately, it doesn't take long to convert a regular form into a scrollable one. You need a couple of scrollbar controls, plus a PictureBox control that you use as the container for all the controls on the form, and a filler controla CommandButton, for examplethat you place in the bottom-right corner of the form when it displays the two scroll bars. The secret to creating scrollable forms is that you don't move all the child controls one by one. Instead, you place all the controls in the PictureBox control (named picCanvas in the following code), and you move it when the user acts on the scroll bar:

51

Sub MoveCanvas() picCanvas.Move -HScroll1.Value, -VScroll1.Value End Sub In other words, to uncover the portion of the form near the right border, you assign a negative value to the PictureBox's Left property, and to display the portion near the form's bottom border you set its Top property to a negative value. It's really that simple. You do this by calling the MoveCanvas procedure from within the scroll bars' Change and Scroll events. Of course, it's critical that you write code in the Form_Resize event, which makes a scroll bar appear and disappear as the form is resized, and that you assign consistent values to Max properties of the scrollbar controls: ' size of scrollbars in twips Const SB_WIDTH = 300 ' width of vertical scrollbars Const SB_HEIGHT = 300 ' height of horizontal scrollbars Private Sub Form_Resize() ' Resize the scroll bars along the form. HScroll1.Move 0, ScaleHeight - SB_HEIGHT, ScaleWidth - SB_WIDTH VScroll1.Move ScaleWidth - SB_WIDTH, 0, SB_WIDTH, _ ScaleHeight - SB_HEIGHT cmdFiller.Move ScaleWidth - SB_WIDTH, ScaleHeight - SB_HEIGHT, _ SB_WIDTH, SB_HEIGHT ' Put these controls on top. HScroll1.ZOrder VScroll1.ZOrder cmdFiller.ZOrder picCanvas.BorderStyle = 0 ' A click on the arrow moves one pixel. HScroll1.SmallChange = ScaleX(1, vbPixels, vbTwips) VScroll1.SmallChange = ScaleY(1, vbPixels, vbTwips) ' A click on the scroll bar moves 16 pixels. HScroll1.LargeChange = HScroll1.SmallChange * 16 VScroll1.LargeChange = VScroll1.SmallChange * 16 ' If the form is larger than the picCanvas picture box, ' we don't need to show the corresponding scroll bar. If ScaleWidth < picCanvas.Width + SB_WIDTH Then HScroll1.Visible = True HScroll1.Max = picCanvas.Width + SB_WIDTH - ScaleWidth Else HScroll1.Value = 0 HScroll1.Visible = False End If If ScaleHeight < picCanvas.Height + SB_HEIGHT Then VScroll1.Visible = True VScroll1.Max = picCanvas.Height + SB_HEIGHT - ScaleHeight Else VScroll1.Value = 0 VScroll1.Visible = False End If ' Make the filler control visible only if necessary. cmdFiller.Visible = (HScroll1.Visible Or VScroll1.Visible) MoveCanvas End Sub

52

Working with scrollable forms at design time isn't comfortable. I suggest that you work with a maximized form and with the PictureBox control sized as large as possible. When you're finished with the form interface, resize the PictureBox control to the smallest area that contains all the controls, and then reset the form's WindowState property to 0-Normal.

Control Arrays
A control array is a group of controls that share the same name type and the same event procedures. Adding controls with control arrays uses fewer resources than adding multiple control of same type at design time. A control array can be created only at design time, and at the very minimum at least one control must belong to it. You create a control array following one of these three methods: You create a control and then assign a numeric, non-negative value to its Index property; you have thus created a control array with just one element. You create two controls of the same class and assign them an identical Name property. Visual Basic shows a dialog box warning you that there's already a control with that name and asks whether you want to create a control array. Click on the Yes button. You select a control on the form, press Ctrl+C to copy it to the clipboard, and then press Ctrl+V to paste a new instance of the control, which has the same Name property as the original one. Visual Basic shows the warning mentioned in the previous bullet. Control arrays are one of the most interesting features of the Visual Basic environment, and they add a lot of flexibility to your programs: Controls that belong to the same control array share the same set of event procedures; this often dramatically reduces the amount of code you have to write to respond to a user's actions. You can dynamically add new elements to a control array at run time; in other words, you can effectively create new controls that didn't exist at design time. Elements of control arrays consume fewer resources than regular controls and tend to produce smaller executables. Besides, Visual Basic forms can host up to 256 different control names, but a control array counts as one against this number. In other words, control arrays let you effectively overcome this limit. The importance of using control arrays as a means of dynamically creating new controls at run time is somewhat reduced in Visual Basic 6, which has introduced a new and more powerful capability. Don't let the term array lead you to think control array is related to VBA arrays; they're completely different objects. Control arrays can only be one-dimensional. They don't need to be dimensioned: Each control you add automatically extends the array. The Index property identifies the position of each control in the control array it belongs to, but it's possible for a control array to have holes in the index sequence. The lowest possible value for the Index property is 0. You reference a control belonging to a control array as you would reference a standard array item: Text1(0).Text = ""

Sharing Event Procedures


Event procedures related to items in a control array are easily recognizable because they have an extra Index parameter, which precedes all other parameters. This extra parameter receives the index of the element that's raising the event, as you can see in this example: Private Sub Text1_KeyPress(Index As Integer, KeyAscii As Integer) MsgBox "A key has been pressed on Text1(" & Index & ") control" End Sub

53

The fact that multiple controls can share the same set of event procedures is often in itself a good reason to create a control array. For example, say that you want to change the background color of each of your TextBox controls to yellow when it receives the input focus and restore its background color to white when the user clicks on another field: Private Sub Text1_GotFocus(Index As Integer) Text1(Index).BackColor = vbYellow End Sub Private Sub Text1_LostFocus(Index As Integer) Text1(Index).BackColor = vbWhite End Sub Control arrays are especially useful with groups of OptionButton controls because you can remember which element in the group has been activated by adding one line of code to their shared Click event. This saves code when the program needs to determine which button is the active one: ' A module-level variable Dim optFrequencyIndex As Integer Private Sub optFrequency_Click(Index As Integer) ' Remember the last button selected. optFrequencyIndex = Index End Sub

Creating Controls at Run Time


Control arrays can be created at run time using the statements Load object (Index %) Unload object (Index %)

Where object is the name of the control to add or delete from the control array. Index % is the value of the index in the array. The control array to be added must be an element of the existing array created at design time with an index value of 0. When a new element of a control array is loaded, most of the property settings are copied from the lowest existing element in the array. Following example illustrates the use of the control array. * Open a Standard EXE project and save the Form as Calculator.frm and save the Project as Calculater.vbp. * Design the form as shown below. Object Property Caption Name Caption CommandButton Name Index CommandButton Caption Setting Calculator frmCalculator 1 cmd 0 2

Form

54

Name Index Caption CommandButton Name Index Caption CommandButton Name Index Caption CommandButton Name Index Caption CommandButton Name Index Caption CommandButton Name Index Caption CommandButton Name Index Caption CommandButton Name Index Caption CommandButton Name Index Caption CommandButton Name Index CommandButton Caption

cmd 1 3 cmd 2 4 cmd 3 5 cmd 4 6 cmd 5 7 cmd 6 8 cmd 7 9 cmd 8 0 cmd 10 . cmd 11 AC

55

Name CommandButton Caption Name CommandButton Caption Name CommandButton Caption Name CommandButton Caption Name CommandButton Caption Name TextBox Name Text CommandButton Caption Name

cmdAC + cmdPlus cmdMinus * cmdMultiply / cmdDivide +/cmdNeg txtDisplay ( empty ) = cmdEqual

The following variables are declared inside the general declaration Dim Current As Double Dim Previous As Double Dim Choice As String Dim Result As Double The following code is entered in the cmd_Click( ) (Control Array) event procedure Private Sub cmd_Click(Index As Integer) txtDisplay.Text = txtDisplay.Text & cmd(Index).Caption '&is the concatenation operator Current = Val(txtDisplay.Text) End Sub The following code is entered in the cmdAC_Click ( ) event procedure

56

Private Sub cmdAC_Click() Current = Previous = 0 txtDisplay.Text = "" End Sub The below code is entered in the cmdNeg_Click( ) procedure Private Sub cmdNeg_Click() Current = -Current txtDisplay.Text = Current End Sub The following code is entered in the click events of the cmdPlus, cmdMinus, cmdMultiply, cmdDevide controls respectively. Private Sub cmdDevide_Click() txtDisplay.Text = "" Previous = Current Current = 0 Choice = "/" End Sub Private Sub cmdMinus_Click() txtDisplay.Text = "" Previous = Current Current = 0 Choice = "-" End Sub Private Sub cmdMultiply_Click() txtDisplay.Text = "" Previous = Current Current = 0 Choice = "*" End Sub Private Sub cmdPlus_Click() txtDisplay.Text = "" Previous = Current Current = 0 Choice = "+" End Sub To print the result on the text box, the following code is entered in the cmdEqual_Click ( ) event procedure. Private Sub cmdEqual_Click() Select Case Choice Case "+" Result = Previous + Current txtDisplay.Text = Result Case "-" Result = Previous - Current txtDisplay.Text = Result Case "*" Result = Previous * Current

57

txtDisplay.Text = Result Case "/" Result = Previous / Current txtDisplay.Text = Result End Select Current = Result End Sub Save and run the project. On clicking digits of user's choice and an operator button, the output appears.

Iterating on the Items of a Control Array


Control arrays often let you save many lines of code because you can execute the same statement, or group of statements, for every control in the array without having to duplicate the code for each distinct control. For example, you can clear the contents of all the items in an array of TextBox controls as follows: For i = txtFields.LBound To txtFields.UBound txtFields(i).Text = "" Next Here you're using the LBound and UBound methods exposed by the control array object, which is an intermediate object used by Visual Basic to gather all the controls in the array. In general, you shouldn't use this approach to iterate over all the items in the array because if the array has holes in the Index sequence an error will be raised. A better way to loop over all the items of a control array is using the For Each statement: Dim txt As TextBox For Each txt In txtFields txt.Text = "" Next A third method exposed by the control array object, Count, returns the number of elements it contains. It can be useful on several occasions (for example, when removing all the controls that were added dynamically at run time): ' This code assumes that txtField(0) is the only control that was ' created at design time (you can't unload it at run time). Do While txtFields.Count > 1 Unload txtFields(txtFields.UBound) Loop

Arrays of Menu Items


Control arrays are especially useful with menus because arrays offer a solution to the proliferation of menu Click events and, above all, permit you to create new menus at run time. An array of menu controls is conceptually similar to a regular control array, only you set the Index property to a numeric (non-negative) value in the Menu Editor instead of in the Properties window. There are some limitations, though: All the items in an array of menu controls must be adjacent and must belong to the same menu level, and their Index properties must be in ascending order (even though holes in the sequence are allowed). This set of requirements severely hinders your ability to create new menu items at run time. In fact, you can create new menu items in well-defined positions of your menu hierarchynamely, where you put a menu item with a nonzero Index valuebut you can't create new submenus or new top-level menus.

58

Now that you have a thorough understanding of how Visual Basic's forms and controls work, you're ready to dive into the subtleties of the Visual Basic for Applications (VBA) language.

DriveListBox, DirListBox, And FileListBox Controls


Three of the controls on the ToolBox let you access the computer's file system. They are DriveListBox, DirListBox and FileListBox controls (see below figure) , which are the basic blocks for building dialog boxes that display the host computer's file system. Using these controls, user can traverse the host computer's file system, locate any folder or files on any hard disk, even on network drives. The files are controls are independent of one another, and each can exist on it's own, but they are rarely used separately. The files controls are described next. In a nutshell, the DriveListBox control is a combobox-like control that's automatically filled with your drive's letters and volume labels. The DirListBox is a special list box that displays a directory tree. The FileListBox control is a specialpurpose ListBox control that displays all the files in a given directory, optionally filtering them based on their names, extensions, and attributes. These controls often work together on the same form; when the user selects a drive in a DriveListBox, the DirListBox control is updated to show the directory tree on that drive. When the user selects a path in the DirListBox control, the FileListBox control is filled with the list of files in that directory. These actions don't happen automatically, howeveryou must write code to get the job done. After you place a DriveListBox and a DirListBox control on a form's surface, you usually don't have to set any of their properties; in fact, these controls don't expose any special property, not in the Properties window at least. The FileListBox control, on the other hand, exposes one property that you can set at design timethe Pattern property. This property indicates which files are to be shown in the list area: Its default value is *.* (all files), but you can enter whatever specification you need, and you can also enter multiple specifications using the semicolon as a separator. You can also set this property at run time, as in the following line of code: File1.Pattern = "*.txt;*.doc;*.rtf" Following figure shows three files controls are used in the design of Forms that let users explore the entire structure of their hard disks.

DriveListBox : Displays the names of the drives within and connected to the PC. The basic property of this control is the drive property, which set the drive to be initially selected in the control or returns the user's selection. DirListBox : Displays the folders of current Drive. The basic property of this control is the Path property, which is the name of the folder whose sub folders are displayed in the control. FileListBox : Displays the files of the current folder. The basic property of this control is also called Path, and it's the path name of the folder whose files are displayed.

59

The three File controls are not tied to one another. If you place all three of them on a Form, you will see the names of all the folders under the current folder, and so on. Each time you select a folder in the DirlistBox by double clicking its name, its sub folders are displayed. Similarly , the FileListBox control will display the names of all files in the current folder. Selecting a drive in the DriveListBox control, however this doesn't affect the contents of the DirListBox. To connect to the File controls, you must assign the appropriate values to the properties. To compel the DirListBox to display the folders of the selected drive in the DriveListBox, you must make sure that each time the user selects another drive, the Path property of the DirListBox control matches the Drive property of the DriveListBox. After these preliminary steps, you're ready to set in motion the chain of events. When the user selects a new drive in the DriveListBox control, it fires a Change event and returns the drive letter (and volume label) in its Drive property. You trap this event and set the DirListBox control's Path property to point to the root directory of the selected drive: Private Sub Drive1_Change() ' The Drive property also returns the volume label, so trim it. Dir1.Path = Left$(Drive1.Drive, 1) & ":\" End Sub When the user double-clicks on a directory name, the DirListBox control raises a Change event; you trap this event to set the FileListBox's Path property accordingly: Private Sub Dir1_Change() File1.Path = Dir1.Path End Sub Finally, when the user clicks on a file in the FileListBox control, a Click event is fired (as if it were a regular ListBox control), and you can query its Filename property to learn which file has been selected. Note how you build the complete path: Filename = File1.Path If Right$(Filename, 1) <> "\" Then Filename = Filename & "\" Filename = Filename & File1.Filename The DirListBox and FileListBox controls support most of the properties typical of the control they derive fromthe ListBox controlincluding the ListCount and the ListIndex properties and the Scroll event. The FileListBox control supports multiple selection; hence you can set its MultiSelect property in the Properties window and query the SelCount and Selected properties at run time. The FileListBox control also exposes a few custom Boolean properties, Normal, Archive, Hidden, ReadOnly, and System, which permit you to decide whether files with these attributes should be listed. (By default, the control doesn't display hidden and system files.) This control also supports a couple of custom events, PathChange and PatternChange, that fire when the corresponding property is changed through code. In most cases, you don't have to worry about them, and I won't provide examples of their usage. The problem with the DriveListBox, DirListBox and FileListBox controls is that they're somewhat outdated and aren't used by most commercial applications any longer. Moreover, these controls are known to work incorrectly when listing files on network servers and sometimes even on local disk drives, especially when long file and directory names are used. For this reason, I discourage you from using them and suggest instead that you use the Common Dialog controls for your FileOpen and FileSave dialog boxes. But if you need to ask the user for the name of a directory rather than a file, you're out of luck becausewhile Windows does include such a system dialog box, named BrowseForFolders dialogVisual Basic still doesn't offer a way to display it (unless you do some advanced API programming). Fortunately, Visual Basic 6 comes with a new controlthe ImageCombo controlthat lets you simulate the appearance of the DriveListBox control. It also offers you a powerful librarythe FileSystemObject librarythat completely frees you from using these three controls, if only as hidden controls that you use just for quickly retrieving information on the file system.

Using A CheckBox Control

60

The CheckBox control is similar to the option button, except that a list of choices can be made using check boxes where you cannot choose more than one selection using an OptionButton. By ticking the CheckBox the value is set to True. This control can also be grayed when the state of the CheckBox is unavailable, but you must manage that state through code. When you place a CheckBox control on a form, all you have to do, usually, is set its Caption property to a descriptive string. You might sometimes want to move the little check box to the right of its caption, which you do by setting the Alignment property to 1-Right Justify, but in most cases the default setting is OK. If you want to display the control in a checked state, you set its Value property to 1-Checked right in the Properties window, and you set a grayed state with 2Grayed. The only important event for CheckBox controls is the Click event, which fires when either the user or the code changes the state of the control. In many cases, you don't need to write code to handle this event. Instead, you just query the control's Value property when your code needs to process user choices. You usually write code in a CheckBox control's Click event when it affects the state of other controls. For example, if the user clears a check box, you might need to disable one or more controls on the form and reenable them when the user clicks on the check box again. This is how you usually do it (here I grouped all the relevant controls in one frame named Frame1): Private Sub Check1_Click() Frame1.Enabled = (Check1.Value = vbChecked) End Sub Note that Value is the default property for CheckBox controls, so you can omit it in code. I suggest that you not do that, however, because it would reduce the readability of your code. The following example illustrates the use of CheckBox control * Open a new Project and save the Form as CheckBox.frm and save the Project as CheckBox.vbp * Design the Form as shown below Object Form Name Caption CheckBox Name Caption CheckBox Name Caption CheckBox Name Caption OptionButton Name Caption OptionButton Name Caption OptionButton Name optGreen optBlue Green optRed Blue chkUnderline Red chkItalic Underline chkBold Italic frmCheckBox Bold Property Caption Setting CheckBox

61

Name TextBox Text Caption CommandButton Name

txtDisplay (empty) Exit cmdExit

Following code is typed in the Click() events of the CheckBoxes Private Sub chkBold_Click() If chkBold.Value = 1 Then txtDisplay.FontBold = True Else txtDisplay.FontBold = False End If End Sub Private Sub chkItalic_Click() If chkItalic.Value = 1 Then txtDisplay.FontItalic = True Else txtDisplay.FontItalic = False End If End Sub Private Sub chkUnderline_Click() If chkUnderline.Value = 1 Then txtDisplay.FontUnderline = True Else txtDisplay.FontUnderline = False End If End Sub Following code is typed in the Click() events of the OptionButtons Private Sub optBlue_Click() txtDisplay.ForeColor = vbBlue End Sub Private Sub optRed_Click() txtDisplay.ForeColor = vbRed End Sub

62

Private Sub optGreen_Click() txtDisplay.ForeColor = vbGreen End Sub To terminate the program following code is typed in the Click() event of the Exit button Private Sub cmdExit_Click() End End Sub Run the program by pressing F5. Check the program by clicking on OptionButtons and CheckBoxes.

Working With Forms

The Appearance of Forms


The main characteristic of a Form is the title bar on which the Form's caption is displayed. On the left end of the title bar is the Control Menu icon. Clicking this icon opens the Control Menu. Maximize, Minimize and Close buttons can be found on the right side of the Form. Clicking on these buttons performs the associated function. The following figure illustrates the appearance of a Form

The control menu contains the following commands : Restore : Restores a maximized Form to the size it was before it was maximized; available only if the Form has been maximized. Move : Lets the user moves the Form around with the mouse Size : Lets the user resizes the control with the mouse Minimize: Minimizes the Form Maximize : Maximizes the Form Close : Closes the Form

Setting the Start-Up Form


A typical application has more than a single Form. When an application runs the main Form is loaded. By setting the Project properties you can control which Form is to be displayed in the Start-Up of the application. Following figure illustrates the Project property window.

63

By default, Visual Basic suggests the name of the first Form created when the project started.

Loading and Unloading Forms


In order to load and unload the forms, Load and Unload statements are used. The Load statement has the following syntax : Load FormName And the Unload statement has the following syntax : Unload FormName The FormName variable is the name of the Form to be loaded or unloaded. Unlike the Show method which cares of both loading and displaying the Form, the load statement doesn't show the Form. You have to call the Form's Show method to display it on the desktop.

Showing and Hiding Forms


Show method is used to Show a Form. If the Form is loaded but invisible, the Show method is used to bring the Form on Top every other window. If the Form is not loaded, the Show method loads it and then displays it. Syntax of the Show method of the Form FormName.Show mode The FormName variable is the Form's name, and the optional argument mode determines whether the Form will be Modal or not. It can have one of the following syntax : * 0-Modeless (default) * 1-Modal Modeless Forms are the normal Forms. Modeless Forms interact with the user and the user allowed to switch to any other Form of the application. If you do not specify the optional mode argument, by default the mode is set to modeless. The Modal Forms takes the total control of the application where user cannot switch to any other Forms in the application unless the Form is closed. A modal Form, thus, must have a Close button or some means to close the Form in order to return to the Form where the Modal Form was loaded.

64

Hiding Forms
The Hide method is used to hide a Form. The following is the syntax of the Hide Method. FormName.Hide To hide a Form from within its own code, the following code can be used. Me.Hide You must understand that the Forms that are hidden are not unloaded ; they remains in the memory and can be displayed instantly with the Show Method. When a Form is hidden, you can still access its properties and code. For instance, you can change the settings of its Control Properties or call any Public functions in the Form. The following is an example illustrates the Show method and Mode statement * Open a new Project and save the Project Design the application as shown below Object Property Caption Name Form Caption Name Form Caption Name Label Caption Name Form1 frm1 Form2 frm2 Form3 frm3 Click on a button to display a Form Label1 Setting

Form

The following code is typed in the Click event of the command buttons

65

Run the application. Clicking on the buttons will display the Forms respectively. But you can see that in the cmd2_Click( ) event additionally VbModal argument has been added. You can see the difference after you display the forms by clicking on the command buttons. You can notice that you cannot switch to any other Forms in the application unless you close the Form3. (Download the source code)

Finding out the difference between Unload and Hide method


To know what the difference is between Unload and Hide methods we will do an example. Open a new project and save the project. Draw two buttons on the form and name those as shown above.

In the click event of the Hide button Following code is entered. Me.Hide In the click event of the Unload button following code is entered. Unload Me Save the project and run the application. Once you click on Hide button you can note that the Form is invisible but the application is still running. But when you click on Unload button you can see that the application is terminated.

Working With Menus


Windows applications provide groups of related commands in Menus. These commands depends on the application, but some-such as Open and Save are frequently found in applications. Menus are intrinsic controls, and as such they deserve a place in this chapter. On the other hand, menus behave differently from other controls. For example, you don't drop menu items on a form from the Toolbox; rather, you design them in the Menu Editor window, as you can see in the figur below. You invoke this tool from the Menu Editor button on the standard toolbar or by pressing the Ctrl+E shortcut key. There's also a Menu Editor command in the Tools menu, but you probably won't use it often.

66

Visual Basic provides an easy way to create menus with the modal Menu Editor dialog. The below dialog is displayed when the Menu Editor is selected in the Tool Menu. The Menu Editor command is grayed unless the form is visible. And also you can display the Menu Editor window by right clicking on the Form and selecting Menu Editor. Basically, each menu item has a Caption property (possibly with an embedded & character to create an access key) and aName. Each item also exposes three Boolean properties, Enabled, Visible, and Checked, which you can set both at design time and at run time. At design time, you can assign the menu item a shortcut key so that your end users don't have to go through the menu system each time they want to execute a frequent command. (Do you really like pulling down the Edit menu any time you need to clear some text or copy it to the Clipboard?) The assigned shortcut key can't be queried at run time, much less modified. Building a menu is a simple, albeit more tedious, job: You enter the item's Caption and Name, set other properties (or accept the default values for those properties), and press Enter to move to the next item. When you want to create a submenu, you press the Right Arrow button (or the Alt+R hot key). When you want to return to work on top-level menus those items that appear in the menu bar when the application runsyou click the Left Arrow button (or press Alt+L). You can move items up and down in the hierarchy by clicking the corresponding buttons or the hot keys Alt+U and Alt+B, respectively. You can create up to five levels of submenus (six including the menu bar), which are too many even for the most patient user. If you find yourself working with more than three menu levels, think about trashing your specifications and redesigning your application from the ground up. You can insert a separator bar using the hypen (-) character for the Caption property. But even these separator items must be assigned a unique value for the Name property, which is a real nuisance. If you forget to enter a menu item's Name, the Menu Editor complains when you decide to close it. The convention used in this book is that all menu names begin with the three letters mnu. An expanded Menu Editor window.

An expanded menu

67

One of the most annoying defects of the Menu Editor tool is that it doesn't permit you to reuse the menus you have already written in other applications. It would be great if you could open another instance of the Visual Basic IDE, copy one or more menu items to the clipboard, and then paste those menu items in the application under development. You can do that with controls and with pieces of code, but not with menus! The best thing you can do in Visual Basic is load the FRM file using an editor such as Notepad, find the portion in the file that corresponds to the menu you're interested in, load the FRM file you're developing (still in Notepad), and paste the code there. This isn't the easiest operation, and it's also moderately dangerous: If you paste the menu definition in the wrong place, you could make your FRM form completely unreadable. Therefore, always remember to make backup copies of your forms before trying this operation. Better news is that you can add a finished menu to a form in your application with just a few mouse clicks. All you have to do is activate the Add-In Manager from the Add-Ins menu, choose the VB 6 Template Manager, and tick the Loaded/Unloaded check box. After you do that, you'll find three new commands in the Tools menu: Add Code Snippet, Add Menu, and Add Control Set. Visual Basic 6 comes with a few menu templates, as you can see in the following figure, that you might find useful as a starting point for building your own templates. To create your menu templates, you only have to create a form with the complete menu and all the related code and then store this form in the \Templates\Menus directory. (The complete path, typically c:\Program Files\Microsoft Visual Studio\VB98\Template, can be found in the Environment tab of the Options dialog box on the Tools menu. The Template Manager was already available with Visual Basic 5, but it had to be installed manually and relatively few programmers were aware of its existence.

The Template Manager in action The programmer can create menu control arrays. The Index TextBox specifies the menu's index in the control array. The Menu Editor dialog also provides several CheckBoxes to control the appearance of the Menu. Checked : This is unchecked by default and allows the programmer the option of creating a checked menu item( a menu item that act as a toggle and displays a check mark when selected. The following is a Check Menu items.

68

Enabled : specifies whether a menu is disabled or not. If you see a disabled command in a menu that means that feature is not available. The Visible checkbox specifies whether the menu is visible or not. To add commands to the Form's menu bar, enter a caption and a name for each command. As soon as you start typing the command's caption, it also appears in a new line in the list at the bottom of the Menu Editor window. To add more commands click Enter and type the Caption and the Name.

Creating Menus
Open a new Project and save the form as menu.frm and save the project as menu.vbp. Choose Tools Menu Editor and type the menu items as shown below.
Caption Name

File Open Save Exit Edit Copy Cut Paste

mnuFile mnuOpen mnuSave mnuExit mnuEdit mnuCopy mnuCut mnuPaste

69

Run the application by pressing F5. You can see that you can select a menu.

Accessing Menus At Run Time In Visual Basic 6


Menu controls expose only one event, Click. As you expect, this event fires when the user clicks on the menu: Private Sub mnuFileExit_Click() Unload Me End Sub You can manipulate menu items at run time through their Checked, Visible, and Enabled properties. For example, you can easily implement a menu item that acts as a switch and displays or hides a status bar: Private Sub mnuViewStatus_Click() ' First, add or remove the check sign. mnuViewStatus.Checked = Not mnuViewStatus.Checked ' Then make the status bar visible or not. staStatusBar.Visible = mnuViewStatus.Checked End Sub While menu items can be responsible for their own Checked status, you usually set their Visible and Enabled properties in another region of the code. You make a menu item invisible or disabled when you want to make the corresponding command unavailable to the user. You can choose from two different strategies to achieve this goal: You can set the menu properties as soon as something happens that affects that menu command, or you can set them one instant before the menu is dropped down. Let me explain these strategies with two examples. Let's say that the Save command from the File menu should look disabled if your application has loaded a read-only file. In this case, the most obvious place in code to set the menu Enabled property to False is in the procedure that loads the file, as shown in the code below. Private Sub LoadDataFile(filename As String) ' Load the file in the program. ' ... (code omitted)... ' Enable or disable the menu enabled state according to the file's ' read-only attribute (no need for an If...Else block).

70

mnuFileSave.Enabled = (GetAttr(filename) And vbReadOnly) End Sub This solution makes sense because the menu state doesn't change often. By comparison, the state of most of the commands in a typical Edit menu (Copy, Cut, Clear, Undo, and so on) depends on whether any text is currently selected in the active control. In this case, changing the menu state any time a condition changes (because the user selects or deselects text in the active control, for example) is a waste of time, and it also requires a lot of code. Therefore, it's preferable to set the state of those menu commands in the parent menu's Click event just before displaying the menu: Private Sub mnuEdit_Click() ' The user has clicked on the Edit menu, ' but the menu hasn't dropped down yet. On Error Resume Next ' Error handling is necessary because we don't know if ' the Active control actually supports these properties. mnuEditCopy.Enabled = (ActiveControl.SelText <> "") mnuEditCut.Enabled = (ActiveControl.SelText <> "") mnuEditClear.Enabled = (ActiveControl.SelText <> "") End Sub

Creating Pop-Up Menus In Visual Basic 6


Visual Basic also supports pop-up menus, those context-sensitive menus that most commercial applications show when you right-click on an user interface object. In Visual Basic, you can display a pop-up menu by calling the form's PopupMenu method, typically from within the MouseDown event procedure of the object: Private Sub List1_MouseDown(Button As Integer, Shift As Integer, _ X As Single, Y As Single) If Button And vbRightButton Then ' User right-clicked the list box. PopupMenu mnuListPopup End If End Sub The argument you pass to the PopupMenu method is the name of a menu that you have defined using the Menu Editor. This might be either a submenu that you can reach using the regular menu structure or a submenu that's intended to work only as a pop-up menu. In the latter case, you should create it as a top-level menu in the Menu Editor and then set its Visible attribute to False. If your program includes many pop-up menus, you might find it convenient to add one invisible top-level entry and then add all the pop-up menus below it. (In this case, you don't need to make each individual item invisible.) The complete syntax of the PopupMenu method is quite complex: PopupMenu Menu, [Flags], [X], [Y], [DefaultMenu] By default, pop-up menus appear left aligned on the mouse cursor, and even if you use a right-click to invoke the menu you can select a command only with the left button. You can change these defaults using the Flags argument. The following constants control the alignment: 0-vbPopupMenuLeftAlign (default), 4-vbPopupMenuCenterAlign, and 8vbPopupMenuRightAlign. The following constants determine which buttons are active during menu operations: 0vbPopupMenuLeftButton (default) and 2-vbPopupMenuRightButton. For example, I always use the latter because I find it natural to select a command with the right button since it's already pressed when the menu appears: PopupMenu mnuListPopup, vbPopupMenuRightButton The x and y arguments, if specified, make the menu appear in a particular position on the form, rather than at mouse coordinates. The last optional argument is the name of the menu that's the default item for the pop-up menu. This item will be displayed in boldface. This argument has only a visual effect; If you want to offer a default menu item, you must write code in the MouseDown event procedure to trap double-clicks with the right button.

71

You can take advantage of the x and y arguments in a PopupMenu method to make your program more Windows compliant, and show your pop-up menus over the control that has the focus when the user presses the Application key (the key beside the Windows key on the right side of a typical extended keyboard, such as the Microsoft Natural Keyboard). But remember that Visual Basic doesn't define any key-code constant for this key. Here's how you must proceed: Private Sub List1_KeyDown(KeyCode As Integer, Shift As Integer) If KeyCode = 93 Then ' The system pop-up menu key has been pressed. ' Show a pop-up menu near the list box's center. PopupMenu mnuListPopup, , List1.Left + _ List1.Width / 2, List1.Top + List1.Height / 2 End If End Sub Visual Basic's implementation of pop-up menus has a serious flaw. All Visual Basic TextBox controls react to right-clicks by showing the standard Edit pop-up menu (with the usual commands, such as Undo, Copy, Cut, and so on). The problem is that if you invoke a PopupMenu method from within the TextBox control's MouseDown event, your custom pop-up menu will be displayed only after the standard one, which is obviously undesirable. You can solve it only by resorting to the unorthodox and undocumented technique shown below. Private Sub Text1_MouseDown(Button As Integer, _ Shift As Integer, X As Single, Y As Single) If Button And vbRightButton Then Text1.Enabled = False PopupMenu mnuMyPopup Text1.Enabled = True End If End Sub

The Multiple Document Interface (MDI) In Visual Basic 6


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.

72

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

73

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.

And as soon as you click MDI Open you can notice that the main menu of the MDI Form is replaced with the Menu of the Child Form. The reason for this behavior should be obvious. The operation available through the MDI Form are quite different from the operations of the child window. Moreover, each child Form shouldn't have it's own menu.

InputBox Function In Visual Basic 6


Displays a prompt in a dialog box, waits for the user to input text or click a button, and returns a String containing the contents of the text box. Following is an expanded InputBox

Syntax : memory_variable = InputBox (prompt[,title][,default]) memory_variable is a variant data type but typically it is declared as string, which accept the message input by the users. The arguments are explained as follows: Prompt - String expression displayed as the message in the dialog box. If prompt consists of more than one line, you can separate the lines using the vbCrLf constant Title - String expression displayed in the title bar of the dialog box. If you omit the title, the application name is displayed in the title bar default-text - The default text that appears in the input field where users can use it as his intended input or he may change to the message he wish to key in. x-position and y-position - the position or the coordinate of the input box.

Following example demonstrates the use of InputBox function * Open a new project and save the Form as InputBox.frm and save the Project as InputBox.vbp * Design the application as shown below.

74

Object Form

Property Caption Name

Setting InputBox test frmInputBox You entered lbl1 ( empty) lbl2 1-Fixed Single OK cmdOK

Label

Caption Name Caption

Label Name BorderStyle CommandButton Caption Name

Following code is entered in cmdOK_Click ( ) event Private Sub cmdok_Click() Dim ans As String ans = InputBox("Enter something to be displayed in the label", "Testing", 0) If ans = "" Then lbl2.Caption = "No message" Else lbl2.Caption = ans End If End Sub Save and run the application. As soon as you click the OK button you will get the following InputBox

75

Here I have entered "Hello World" in text field. As soon as you click OK the output is shown as shown below

MessageBox Function In Visual Basic 6


Displays a message in a dialog box and wait for the user to click a button, and returns an integer indicating which button the user clicked. Following is an expanded MessageBox

Syntax : MsgBox ( Prompt [,icons+buttons ] [,title ] ) memory_variable = MsgBox ( prompt [, icons+ buttons] [,title] ) Prompt : String expressions displayed as the message in the dialog box. If prompt consist of more than one line, you can separate the lines using the vbrCrLf constant. Icons + Buttons : Numeric expression that is the sum of values specifying the number and type of buttons and icon to display. Title : String expression displayed in the title bar of the dialog box. If you omit title, the application name is placed in the title bar. Icons Constant vbCritical vbQuestion vbExclamation vbInformation Value 16 32 48 64 Description Display Critical message icon Display Warning Query icon Display Warning message icon Display information icon

76

Buttons Constant vbOkOnly vbOkCancel vbAbortRetryIgnore vbYesNoCancel vbYesNo vbRetryCancel Return Values Constant vbOk vbCancel vbAbort vbRetry vbIgnore vbYes vbNo Value 1 2 3 4 5 6 7 Ok Button Cancel Button Abort Button Retry Button Ignore Button Yes Button No Button Description Value 0 1 2 3 4 5 Description Display OK button only Display OK and Cancel buttons Display Abort, Retry and Ignore buttons Display Yes, No and Cancel buttons Display Yes and No buttons Display Retry and Cancel buttons

Following is an example illustrates the use of message boxes * Open a new Project and save the Form as messageboxdemo.frm and save the Project as messageboxdemo.vbp * Design the application as shown below. Object Form Property Caption Name Label Caption Name TextBox Name Text ListBox CommandButton Name Caption Name CommandButton Caption Name CommandButton Caption Setting MessageBoxDemo frmMessageBoxDemo lblName Name txtName ( empty ) lstName Add cmdAdd Delete cmdDelete Exit

77

Name

cmdExit

Following code is entered in the txtName_Change ( ) event Private Sub txtName_Change() If Len(txtName.Text) > 0 Then cmdAdd.Enabled = True End If End Sub Following code has to be entered in the cmdAdd_Click ( ) event Private Sub cmdAdd_Click() answer = MsgBox("Do you want to add this name to the list box?", vbExclamation + vbYesNo, "Add Confirm") If answer = vbYes Then lstName.AddItem txtName.Text txtName.Text = "" txtName.SetFocus cmdAdd.Enabled = False End If End Sub Following code is entered in the cmdDelete_Click ( ) event Private Sub cmdDelete_Click() Dim remove As Integer remove = lstName.ListIndex If remove < 0 Then MsgBox "No names is selected", vbInformation, "Error" Else answer = MsgBox("Are you sure you want to delete " & vbCrLf & "the selected name?",_ vbCritical + vbYesNo, "Warning") If answer = vbYes Then If remove >= 0 Then lstName.RemoveItem remove txtName.SetFocus MsgBox "Selected name was deleted", vbInformation, "Delete Confirm" End If End If End If End Sub

78

Following code is entered in the cmdExit_Click ( ) event Private Sub cmdExit_Click() answer = MsgBox("Do you want to quit?", vbExclamation + vbYesNo, "Confirm") If answer = vbYes Then End Else MsgBox "Action canceled", vbInformation, "Confirm" End If End Sub Save and run the application. You can notice the different type of message box types are used to perform an action

Mouse Events In Visual Basic 6


Visual Basic responds to various mouse events, which are recognized by most of the controls. The main events areMouseDown, MouseUp and MouseMove. MouseDown occurs when the user presses any mouse button and MouseUp occurs when the user releases any mouse button. These events use the arguments button, Shift, X, Y and they contain information about the mouse's condition when the button is clicked. The first argument is an integer called Button. The value of the argument indicates whether the left, right or middle mouse button was clicked. The second argument in an integer called shift. The value of this argumnet indicates whether the mouse button was clicked simultaneously with the Shift key, Ctrl key or Alt key. The third and fourth arguments X and Y are the coordinates of the mouse location at the time the mouse button was clicked. As theForm_MouseDown( ) is executed automatically whenever the mouse button is clicked inside the Form's area the X, Y co-ordinates are referenced to the form.

Positioning a control
MouseDown is the commonly used event and it is combined wiyth the move method to move an Image control to different locations in a Form. The following application illustrates the movement of objects responding to move events. it makes use of two OptionButton Controls, two image controls and a CommandButton. The application is designed in such a way that when an OptionButton is selected, the corresponding image control is placed anywhere in the form whenever it is clicked. Open a new standard EXE project and save the Form as Move.frm and save the project as Move.vbp Design the Form as shown below. Object Form Caption Name OptionButton Caption Name Value OptionButton Caption Name Property Setting MouseDown frmMouseDown Credit card is selected optCredit True Cash is selected optCash

79

Image

Name Picture

imgCredit c:/credit.jpg imgCash c:/cash.jpg

Image

Name Picture

The follwoing code is entered in the general declarations section of the Form. Option Explicit The following code is entered in the Form_MouseDown( ) event Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) If optCredit = True Then imgCredit.Move X, Y Else imgCash.Move X, Y End If End Sub Run the application by keying in F5. You can notice that when the mouse is clicked on the form somewhere, the selected image moves to that clicked location. This is shown in the below figure.

Graphical Mouse Application In Visual Basic 6


The mouse events can be combined with graphics methods and any number of customized drawing or paint applications can be created. The following application combines MouseMove and MouseDown events, and illustrates a drawing program. Open a new Standard EXE project and save the Form as Draw.frm and save the Project as Draw.vbp. Name the caption of the as Drawing. Add command button control and name the caption of it as Clear Enter the following code in the Form_MouseDown ( ) procedure, Form_MouseMove ( ) procedure and cmdClear_Click ( ) procedures respectively. Private Sub cmdClear_Click() frmDraw.Cls End Sub

80

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) frmDraw.CurrentX = X frmDraw.CurrentY = Y End Sub Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single) If Button = 1 Then Line (frmDraw.CurrentX, frmDraw.CurrentY)-(X, Y) End If End Sub Button value 1 indicates that the left mouse button is clicked. The code written in the MouseDown event changes the CurrentX and CurrentY to the coordinates where the mouse button was just clicked. Run the application. You can notice that when the mouse is clicked and moved in the Form a line is drawn corresponding to the mouse movement. Following figure illustrates the combined action of MouseDown and MouseMove.

The program uses two graphics related Visual Basic concepts, the Line method and the CurrentX and CurrentY properties. Line method is preferred to draw a line in a Form. The following statement draws a line from the coordinates X = 2500, Y = 2000, X = 5000, Y = 5500 Line (2500, 2000) - (5000, 5500) The CurrentX and CurrentY properties are not visible in the properties window of the Form because it cannot be set at the design time. After using the Line method to draw a line in a Form, Visual Basic automatically assigns the coordinate of the line's end point to the CurrentX and CurrentY properties of the Form on which the line is drawn.

MouseMove application
Visual Basic does not generate a MouseMove event for every pixel the mouse moves over and a limited number of mouse messages are generated per second by the operating environment. The following application illustrates how often theForm_MouseMove ( ) event is executed. Open a new standard EXE project and save the form as MouseMove.frm and save the Project as MouseMOve.vbp. Place aCommandButton control and name the caption as Clear and set the name as cmdClear. The following code is entered in the cmdClear_Click ( ) and Form_MouseMove ( ) events respectively. Private Sub cmdClear_Click() frmMouseMove.Cls End Sub

81

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single) Circle (X, Y), 70 End Sub The above procedure simply draws small circles at the mouse's current location using the Circle method. The parameter x, y represent the centre of the circle, and the second parameter represents the radius of the circle. Save the application and run. You can notice that when the mouse is moved inside the Form, circles are drwan along the path of the mouse movement as shown in below figure. And also you can notice the circles are widely spaced when the mouse is moved quickly. Each small circle is an indication that the MouseMove event occured and the Form_MouseMove ( )procedure was executed.

Error Handling And Debugging And File Input/Output


Error Handling enables programmers to write clearer, more robust, more fault-tolerant programs. Error handling enables the programmer to attempt to recover (i.e., continue executing) from infrequent fatal errors rather than letting them occur and suffering the consequences (such as loss of application data). If an error is severe and recovery is not possible, the program can be exited "gracefully"-all files can be closed and notification can be given that the program is terminating. The recovery code is called an error handler. Error handling is designed for dealing with synchronous errors such as an attempt to divide by 0 (that occurs as the program executes the divide instruction). Other common examples of synchronous errors are memory exhaustion, an outof-bound array index, and arithmetic overflow. Error handling provides the programmer with a disciplined set of capabilities for dealing with these types of errors. Error-handling code varies in nature and amount among software systems depending on the application and whether or not the software is a product for release. Products tend to contain much more error-handling code than is contained in "casual" software. Usually, error-handling code is interspersed throughout a program's code. Errors are dealt with the places in the code where errors are likely to occur. The advantage of this approach is that a programmer reading the code can see the error handling in the immediate vicinity of the code and determine if the proper error handling has been implemented. The problem with the scheme is that code in a sense becomes "polluted" with error handling. It becomes difficult for a programmer concerned with the application itself to read the code and determine if the code is working is correctly. Error handling often makes the code more difficult to understand and maintain.

82

When Error Handling should be used


Error handling should be used to process only exceptional situations, despite the fact that there is nothing to prevent that programmer from using errors as an alternate form of program control. This lesson explains about error handling and debugging in easy steps with quick examples. This chapter discusses error handling in Visual Basic applications. Being able to trap, identify, and handle errors is a vital part of developing a user-friendly application. Error trapping and error handling can provide important feedback to the user about why an error occurred. The information provided through error handling can also help the developer find, correct, and prevent errors. Trapping and handling errors in an Executable is covered in this chapter as well as dealing with errors at design time while debugging a project. You will learn different ways to trap errors when they occur and to handle different error situations. In this chapter, recommendations for creating common error handling procedures are provided along with examples of how error information should be presented to the user. This chapter covers the following topics: Setting error handling options The Err object Handling errors in code The error handling hierarchy Common error handling routines The Error function The Error statement

Setting Error-Handling Options


The first time you will encounter errors in your application is when you are still testing your application in the VB IDE. No matter how careful you are in coding, you will always find something that you did incorrectlyor a condition you did not anticipatethe first time you run your application. How you want to handle these errors when you run in the development environment depends on several factors. You might want to stop your application and go into Debug mode every time your application generates a runtime error. You may also want to bypass runtime errors or allow any error handling that you have already coded to take control. Visual Basic provides several different options for you to use in the development environment. These choices for handling errors impact your application only while you are in the IDE and do not affect an Executable created with Visual Basic. The choice you make depends on how complete your code is and the type of application you are developing. Setting Break on All Errors Setting Break in Class Modules Setting Break on Unhandled Errors

Setting Break on All Errors


Error-handling options are set in the development environment with the Options dialog box. From the Visual Basic menu, choose Tools and then Options. The Options dialog box appears. The errorhandling options are on the General tab, as shown in Figure 11.1.

83

FIGURE 11.1 The error-handling options available The first option for error handling in the development environment is to Break on All Errors. If this option is selected and a runtime error occurs in your application, the program will stop immediately. Execution will stop regardless of any errorhandling procedures you have in your code (discussed later in "Handling Errors in Code"). The program stops regardless of whether the code that caused the error is in a class module that is part of your Visual Basic environment. Remember that these options are in effect only when you are running your code in the Visual Basic development environment, not for Executables. Breaking on All Errors is usually used when you have very little error handling in your procedures and you expect some runtime errors to occur. It enables you to find and correct the errors while you run your project interactively.

Setting Break in Class Modules


The second option for handling errors at runtime is to Break in Class Modules when there is no error handling present. This option is used for projects that contain class modules, especially projects that are using ActiveX components created as other Visual Basic projects. When debugging ActiveX components, it will be important for you to see the code that generates an error in the ActiveX project. With this option you need to be able to see the line of code that generated an error in a class module. If you did not use this option, errors generated in a class module would return back to the client program instead of breaking in the class module. Breaking in class modules is different than breaking on all errors when error handling is in place. If Break in Class Modules is selected and error handling is in place for the code that generated a runtime error, the error handler would be executed and the continued program execution would depend on that error handler.

Setting Break on Unhandled Errors


The final option for error handling in the IDE is to Break on All Unhandled Errors. For this option if an error occurs and an errorhandling routine is in place, that error handler will be run and execution will continue without entering Break mode. If no error-handling routine is active (see the section "Using the Error-Handling Hierarchy") when a runtime error occurs, the program will go into Break mode. The difference between breaking in class modules and breaking on unhandled errors comes into play when the code that generated a runtime error is in a class module. In Break in Class Module option, if an unhandled error occurred in a class module, Visual Basic would enter Break mode at that line of code in the class module. In the Break on All Unhandled Errors option, if the unhandled error occurs in a class module, Visual Basic enters Break mode at the line of code that referenced the procedure in the class module.

84

Using The Error-Handling Hierarchy


Unless you code error-handling routines in every procedure you write, Visual Basic needs a way of finding that error handler when a runtime error occurs. This section discusses the methods by which Visual Basic finds that error-handling procedure and what happens if one is not found. Developers of Visual Basic programs control error handling with the On Error statement. The On Error statement dictates how a runtime error will be handled when encountered. If a procedure in your code is executing and an error occurs, Visual Basic looks for On Error within that procedure. If the On Error statement is found, the commands following On Error are executed. If an error handling does not exist in the procedure in which the error occurred, Visual Basic goes up in the calling chain to the code that called the current procedure to look for an On Error statement. Visual Basic continues going up the calling chain until some kind of error handling is found. If there is no procedure in the calling chain with error handling, the application ends with a fatal error. The following example shows how errors will be handled in a simple code example: Private Sub Main() Call SubroutineA End Sub Private Sub SubroutineA() Call SubroutineB End Sub Private Sub SubroutineB() Dim I as Integer Dim J as Integer I=0 J = 10 / I End Sub If you have these three procedures in your application, you can see that this code will generate a runtime error in SubroutineB. The statement J = 10 / I will generate a "divide by zero" error. When this application runs, sub-routine Main starts, and then it calls SubroutineA, which in turn calls SubroutineB. After the runtime error occurs in SubroutineB, Visual Basic must determine how to handle the error. In this case, when the error occurs in SubroutineB, Visual Basic looks in SubroutineB for error handling specified by an On Error statement. Because it is not coded, Visual Basic goes up the calling chain to SubroutineA and checks there for error handling. Again because none is found, Visual Basic goes up the chain to Main. Because Main does not have any error handling either, a fatal error occurs and the application shuts down. Now consider the case where sub-routine Main is modified to look like this: Private Sub Main() On Error GoTo Main_Error Call SubroutineA Exit Sub Main_Error: Msgbox "An error occurred." Resume Next End Sub When this code runs, a division by 0 still occurs in SubroutineB. Visual Basic still checks for error handling in SubroutineB and then in SubroutineA. When it is not found, Visual Basic checks Main for an On Error statement. This time it finds one

85

and does whatever the errorhandling statement instructs. In this case there is a statement instructing that execution continue at the label Main_Error. A message box is displayed, and then a Resume Next statement is encountered. You must be aware of the point that a Resume Next or a Resume statement will send you to after the error occurs. A Resume Next statement will not send you back into SubroutineB to the statement after the divide by 0. It sends you instead to the statement in Sub Main after the line that made the call to the procedure that generated the error. In this case the Exit Sub statement would be executed next. It is important to remember two important things about the errorhandling hierarchy. The first is that Visual Basic will search up the procedure in the calling chain until an error handler is found. As an example look at a case where there are three procedures in the calling chain: ProcA, ProcB, and ProcC. ProcA calls ProcB, which in turn calls ProcC. If an error occurs in ProcC, Visual Basic will look in that procedure for an error handler. If one is not found, Visual Basic will go up the calling chain to ProcB and look for an error handler there. Again if one is not found, Visual Basic will check ProcA. The second thing to remember is that if there is no error handler in the calling chain, a fatal error occurs and the application shuts down.

Using The Err Object


Visual Basic uses the Err object to provide information to your application for error handling. The Err object can be used to retrieve information about the type of runtime error that has occurred. The information contained in the Err object can be used to determine how and whether your application should continue. It can also be used to present information to the user, if either the user can take some action to correct the problem or if error information has to get back to the developer. This section defines and describes the properties and methods of the Err object. You can use the properties to retrieve information about an error that has occurred. They can also be set with other information that can be used by other procedures in an application. The Err object can be manipulated with the methods of the object. In some cases it is necessary to use these methods to pass information between modules. 1. 2. 3. Properties of the Err Object Methods of the Err Object Using the vbObjectError Constant

Properties Of The Err Object


The properties of the Err object identify an error by number, description, and source. These properties are set by whatever generated the error. Usually this is the Visual Basic application or an object used by the Visual Basic application. They can also be set by a Visual Basic developer in class modules to identify errors to external users of those class objects (see the section "Sending Information from a COM Component" in Chapter 12, "Creating a COM Component that Implements Business Rules or Logic"). After an error occurs, the properties of the Err object can be used to identify the problem and to determine what action, if any, should be taken to correct the error. 1. 2. 3. 4. 5. 6. Number Property Description Property Source Property HelpFile Property HelpContext Property LastDLLError Property

Properties Of The Err Object - Number Property

86

The Number property of the Err object identifies an error by number. It is set by Visual Basic when a runtime error occurs. It can also be set in code by a developer to identify a user-defined error. The use of the Err object for user-defined errors are described later in this chapter (see "Sending Information from a COM Component" inChapter 12). When a runtime error occurs, the Err.Number property can be used to determine what that error was and how it should be handled. Some error numbers that Windows generates are described later in this chapter in the section titled "Handling Errors in Code." The Number property contains a long integer value. Visual Basic error numbers and "user-defined" numbers (that is error numbers implemented by the programmer) range from 0 to 65,535. If you define any of your own errors in your application, use numbers below the 65,535 limit specified by Visual Basic. Also, be careful not to use a number already defined by Visual Basic. VB reserves numbers up through 512 for itself, so the practical range available to you for your own "user-defined" errors is 513-65,535 (see "Trappable Errors" later in this chapter). VB documentation further recommends that you add the constant vbObjectError to your error code so that calling routines can definitely identify your error as other than a standard VB error. Microsoft is preparing us for the day when they will need to use errors between 513 and 65,535.
IMPORTANT - Numeric Range For Your Own Error Numbers: Implementing userdefined errors with numbers in the range between 0 and 512 (the range reserved for VB-defined errors) will cause confusion for other developers working with your projects. If you accidentally use an error number that already has meaning to Visual Basic, another developer will not know whether the error should be treated as a user-defined error or as a standard error. Even if you use a number in the range that is not defined in the current release of Visual Basic (513-65,535), it might be implemented in subsequent releases. To avoid confusion, it is best to implement user-defined error numbers in the 513 through 65,535 range, adding the constant offset amount vbObjectError to your error number as discussed above and in the section in this chapter devoted to vbObjectError.

Properties Of The Err Object - Description Property


A brief description of an error is available in the Description property of the Err object. The text corresponds to the error identified by Err.Number. When a runtime error occurs in a Visual Basic application such as a division by 0 or an overflow, the Err.Number and Err.Description properties get set and can be used to handle the error or display information to the user. The easiest way to display error information is with a message box, as follows: Msgbox Err.Number & "-" & Err.Description Where you code this message box depends on how you have implemented error handling for your application (see the section"Handling Errors in Code"). As with the Err.Number, the Err.Description can be set in code. If you set the Err.Number property in your code, you should also set the Err.Description property to provide a description of the error. If you do not set the Description property and you use an error number recognized by Visual Basic, the description will be set for you automatically. If Visual Basic does not recognize the error, it sets the Description property to Application-Defined or Object-Defined Error. This is discussed in more detail later in this section, under the section titled "Raise Method."

Properties Of The Err Object - Source Property


The Source property of the Err object is a string expression of the location at which the error occurred in your application. If an unexpected runtime error occurs, Visual Basic sets the Err.Source for you. If the error occurred in a standard module, the source will be set to the project name. If the error occurred in a class module, Visual Basic uses the project name and the class module name for the source. For errors in class modules, the source will have the form project.class.

87

Like the Number and Description properties, the Source property can be set in code. Even if Visual Basic generates the error and populates the properties of the Err object, you might want to overlay the source with a more descriptive text. Because Visual Basic uses only the project, class, and form names to create the source description, you may want to add to the source to specify a more exact location for the error. You could add the name of the procedure in which the error occurred, for example. The Err.Source property is typically used in an error-handling routine within a procedure or a common error-handling procedure for the application (see the section "Handling Errors in Code").

Properties Of The Err Object - HelpFile Property


If you are generating errors within your code and you want to provide additional helpbeyond the Err.Descriptionfor the user, you could have a help file associated with the error information. The HelpFile property is a string expression that contains the path and filename of the help file. The HelpFile property is used in conjunction with the HelpContext property described in the following section. Together they can be used to provide the user with optional help when an error message is displayed in a message box.

Properties Of The Err Object - HelpContext Property


The HelpContext property of the Err object defines a help topic within the file identified by the HelpFile property. Together the two can be used to add additional help to a message box when an error is displayed to the user. An example of using a help file in conjunction with an error might look like this: Dim Msg As String Err.Number = 61 'Disk Full Err.Description = "Your disk is full. Try another drive." Err.HelpFile = "project1.hlp" Err.HelpContext = 32 'context ID for a topic within the help file Msg = Err.Number & "-" & Err.Description & vbCrLf & _ "To see more information, press F1." MsgBox Msg, , "Error in Project1", Err.HelpFile, Err.HelpContext This displays a message box containing the error number, description, and the option to get help from the help file identified by the HelpFile and HelpContext properties, as shown in Figure 11.2.

FIGURE 11.2 An error message with a Help option.

Properties Of The Err Object - LastDLLError Property


The LastDLLError property is a read-only property used by DLLs to return error information to Visual Basic applications. For versions of Visual Basic prior to 5.0, it is important to know that LastDLLError is only available in 32-bit Windows operating systems. If an error occurs in a DLL, the function or sub-routine will signify the error through a return value or an argument. Check the documentation for that DLL to determine how errors will be identified. If an error does occur, you can check the

88

LastDLLError property of the Err object to get additional information for that error. When the LastDLLError property is set, an error exception is not raised and error handling in your Visual Basic application will not be invoked.
IMPORTANT - LastDLLError Only Available in 32-Bit Code: Because the LastDLLError property is only available in 32-bit Windows operating systems, you must be cautious when developing code that will be conditionally compiled for both 16-bit and 32-bit environments. Make sure that you do not include references to LastDLLError in code that will be compiled for a 16-bit executable.

Methods Of The Err Object


The Err object has two methods that you can invoke in your applications. These methods are also invoked automatically in Visual Basic applications as described in the following sections.

Clear Method
The Clear method of the Err object reinitializes all the Err properties. You can use the Clear method at any time to explicitly reset the Err object. Visual Basic also invokes Clear automatically in the following three situations: When either a Resume or Resume Next statement is encountered. At an Exit Sub, Exit Function, or Exit Property statement. Each time an On Error statement is executed.

Raise Method
The Raise method of the Err object is used to generate errors within code. You can use Raise to create your own runtime errors that will be used elsewhere in your application. The Raise method can also be used to pass error information from a class module to another application that uses objects of that class. The arguments of the Raise method correspond to the properties of the Err object. They are as follows: Number. This is a required argument. It is a long integer that contains the error number. Remember that Visual Basic errors fall between 0 and 65,535, inclusive, and VB reserves error numbers 0-512 for itself. If you are defining any of your own errors, use numbers within the range 513-65,535. Microsoft documentation also recommends adding the constant vbObjectError to your error code, as discussed below in the section on vbObjectError and in "Sending Errors from a COM Component" in Chapter 12. Source. An optional argument identifying where an error occurred. Source is a string property that can contain any information that will help point to the exact location of the problem. It may contain the class module name, form name, and procedure. The standard is to set the Source to project.class. Description. An optional argument describing the error that has occurred. If the description is not set, Visual Basic examines the Number argument to determine whether the error number is recognized (between 0 and 65,535). If Number does map to a Visual Basic error, the Description property is set automatically. If Number does not correspond to a Visual Basic error, the Description is set to Application-Defined or Object- Defined Error. HelpFile. Identifies a help file and a path to the file. This optional argument sets the Err.HelpFile property, which can be used with the HelpContext property, to provide help to the user. If HelpFile is not specified, the path and filename for the Visual Basic Help file is used. HelpContext. Used with the HelpFile argument, the optional HelpContext argument identifies a topic within the HelpFile. If the HelpContext is not specified, Visual Basic uses the help topic of the Visual Basic Help file corresponding to the Number argument (if a topic is available). The Raise method is usually used within class modules. It allows you to generate your own runtime errors to pass information to another application using your application as a server. The Raise method is discussed further in the section titled "Sending Errors from a COM Component" in Chapter 12.

Using The VbObjectError Constant

89

When you return error information from a class module, you must be aware that some special handling is required. If you want to generate a certain numbered error in a class module, you should add a constant number, vbObjectError (a number in the range of negative two billion on most systems), to your error number before you invoke the Raise method. You should do this to signal the error is generated in the class object but raised to the calling routine or application. If you wanted to raise error number 500 in your class module, for example, you would invoke the Err.Raise method and pass vbObjectError + 500 as the error number. The calling program or module interpreting the runtime error generated by the Raise method should then strip off the vbObjectError constant to interpret your error.
NOTE - Error Numbers That You Can Use: Currently not all the numbers between 0 and 66,535 are being used by Visual Basic. Some of the numbers are used; others are being reserved for future use. Using the vbObjectError enables you to define your own error numbers. It also prevents rewrite in the futurewhen later versions of Visual Basic that use more error numbers are released.

Handling Errors In Code


There are several ways to handle errors in your Visual Basic applications. The simplest but least effective way is not to do anything. Let the errors occur. Windows will generate error messages to the user, and the application will shut down. A better way to handle errors is to trap the errors with the Visual Basic statement On Error. By coding On Error, you are handling errors in a more graceful manner. You have the option of trying to correct the problem through code, to bypass the code that caused the error, or to shut down the application if the error is unrecoverable. This section discusses the On Error statement and the different ways of using it in procedures. The discussion examines inline error handling and error-handling routines found at the end of procedures. Some trappable errors will also be covered with recommendations on how to use them and what to look for in code. Using the On Error Statement Inline Error Handling Error-Handling Routines Trappable Errors

Handling Errors In Code - Using The On Error Statement

Handling Errors In Code - Using The On Error Statement


The On Error statement in Visual Basic identifies how errors will be handled for a particular routine. It can be used to turn on and turn off error handling for a procedure and, in some cases, sub-routines and functions called from the procedure in which it is coded (see "Using the Error-Handling Hierarchy"). An On Error statement instructs Visual Basic on what should be done if a runtime error occurs. It is good practice to place error handling in every procedure. Generally it is especially important in any routine prone to errors. These include routines that process database information, routines that read from and write to files, and procedures that perform calculations. If an application contains code that relies on some outside eventssuch as a network connection being available or a disk being ready in a drivethere are always situations that are beyond the control of the developer. For these instances good error-handling routines are very important. Different routines require different types of error handling. The syntax of the On Error statement can be coded several ways, depending on each situation.

Goto <line>
The first way to code the On Error statement is as follows:

90

On Error GoTo Main_Error where Main_Error is a line label in a procedure. This is the most common use of the On Error statement and gives the developer the most control over error handling. A procedure using this format would look something like this: Private Sub Main() On Error GoTo Main_Error ' ... some processing ... Exit Sub Main_Error ' error handling code End Sub As in the preceding procedure, it is generally best to put the On Error statement as the first executable statement in a procedure so that any other lines of code will fall under the control of the On Error statement. In this example when an error occurs anywhere after the On Error statement, execution will continue in the errorhandling code. Error-handling code can contain any Visual Basic statements that can be coded elsewhere. It is important, however, to keep errorhandling code simple to prevent additional errors from occurring. If a runtime error occurs in the error-handling code, then the new error will not be handled in the error handler. Instead, the error will be passed up the call stack until either an error handler is encountered, or until the error becomes a runtime error.

Resume Next
The second way of coding an On Error statement is with a Resume Next clause. With a Resume Next clause, the On Error would look like this: On Error Resume Next Resume Next tells Visual Basic that when a runtime error occurs, ignore the statement that caused the error and continue execution with the next statement.

GoTo 0
The last way to code the On Error statement is with the GoTo 0 clause, as in the following: On Error GoTo 0 This is different from the other On Error statements in that it disables rather than enables error handling for the current routine. Even if there is a line labeled "0", error handling will be disabled. Using the On Error Statement Inline Error Handling Error-Handling Routines Trappable Errors

Handling Errors In Code - Inline Error Handling


Not all error handling occurs at the end of a procedure, and you are not limited to one On Error statement in each subroutine or function you code. Sometimes the error-handling requirements change from the beginning of a routine to the end of a routine. There may be some sections of your code where you expect errors to occur and don't careand there are other lines where you do not expect errors and want to be warned when they occur.

91

You may have a procedure that reads information from a file, for example, does some processing, and exits. If the file is not found, you want to exit the routine. If any other error occurs, you want to display a message box. Your code could look something like this: Private SubA() ' set the initial error handling to go to line SubA_Exit On Error Goto SubA_Exit Dim sFile as String sFile = "filename.dat" ' do some additional processing ' Reset the error handling to go to line SubA_Exit ' for the Open statement. On Error Goto SubA_Exit Open sFile For Input As #1 ' Reset to original error handling On Error Goto SubA_Error ' continue processing... SubA_Exit Exit Sub SubA_Error ' error handling End Sub In this example, the On Error statement is used three times. First at the beginning of the sub-routine, the On Error statement is used to tell Visual Basic to go to the error-handling code at SubA_Error when an error occurs. Then before the Open statement, On Error is used again to say that if any error occurs to go to line SubA_Exit and then to leave the routine. Finally the On Error statement is used again to send errors to the SubA_Error line after the Open statement has run successfully. The number of On Error statements in a procedure is only constrained by the limit on total lines and bytes of code for Visual Basic procedures. In addition, you can use any of the On Error types in the same procedure. You can use the Resume, Resume Next, Goto <line>, and Goto 0 all in the same procedure, and each can be used several times to toggle between different types of error handling. Using the On Error Statement Inline Error Handling Error-Handling Routines Trappable Errors

Handling Errors In Code - Error-Handling Routines


This chapter has already discussed the Err object and has also examined the options for the On Error statement in Visual Basic. In addition, the error-handling routines within sub-routines and functions have been mentioned. This section continues the discussion of errorhandling routines in procedures and recommends some standards for their creation. Error-handling routines are sections of code that are executed in the event of an error. They can range from being very simple with only a few lines of code to being very lengthy. Unlike inline error handling, using an error-handling routine enables you to localize all your exception processing in one place within a procedure. Some things to remember when you code error routines within your Visual Basic procedures include the following:

92

Keep your error-handling routines simple. If another error occurs in your error-handling routine, your application will generate a fatal error and shut down. Error-handling routines are generally found at the end of a procedure. Make sure that you put an Exit Sub or Exit Function statement before the error-handling routine so that during normal execution, your program does not fall through to the error-handling code. Use the On Error Goto <line> statement to control program flow in the event of an error. Anticipate the types of errors that are common to the functionality of your code. This will enable you to handle different errors in different ways. It could also prevent fatal errors from occurring (see "Trappable Errors" later in this section). If you need to display error messages to users, provide information useful to them. A user typically does not understand errors such as Invalid File Format. Instead provide a message that states The file you selected is not an Excel spreadsheet. When you display error messages to the user, also include information that will help youas the developertrack down errors. Include the form, class, or standard module name. Identify the procedure in which the error occurred. Add any other additional information that will help you find and correct problems. Consider logging errors. You might wish to implement some sort of persistent record of errors (writing to a text file or database, for example). This will take the burden for error reporting off the user who could forget or misreport error messages. If you decide to log errors, however, be aware that logging routines themselves might cause runtime errors (since database and text file access are prime sources of runtime errors). Whenever possible, give the user the chance to correct problems. If they try to read a file from a floppy drive and forget to insert a disk, for example, provide an opportunity for them to retry the operation. No matter how hard you try, conditions beyond your control will sometimes cause errors to occur in your application. Using some or all of the preceding tips will provide a more user-friendly environment for your users. These tips help provide more information to the user when an error occurs, and will also make it easier to track down and debug problems in an application. Using the On Error Statement Inline Error Handling Error-Handling Routines/li> Trappable Errors

Handling Errors In Code - Trappable Errors


In parts of your applications, you can anticipate the types of errors that can occur. If your application is reading and writing to disk files, for example, you know that you may encounter some file errors. Disks can be full, the user may not have placed a disk or CD-ROM in a drive, or a disk could be unformatted or corrupt. Errors that you can anticipate and code for in your applications can be found in Visual Basic's Help file. If you search Help for "Trappable Errors," you will get a list of errors for which you can trap in your application (see Figure 11.3).

93

FIGURE 11.3 Visual Basic's Help for Trappable Errors. Help will provide you with a description of the error and the number corresponding to that error. These will be the same number and description found in the Err.Number and Err.Description properties after the runtime error has occurred. If you select an error message from Helpas shown in Figure 11.3you will get further information for that error, as shown in Figure 11.4.

FIGURE 11.4 Detailed Help for Visual Basic errors. It makes sense to anticipate and code for errors that could occur frequently in your application. If you know where errors may occur and what those errors are, you can handle them gracefully.

94

Instead of displaying an error message and ending your application when the user has not placed a disk in a drive, you may want to give the user a chance to use a disk or cancel what he or she was trying to do. You can trap for error number 71, Disk not ready, and prompt the user for his or her input. As part of your error-handling routine, you might include the following: If Err.Number = 71 Then ' Disk Not Ready If Msgbox("Disk not ready. Retry?", vbYesNo) = vbYes Then Resume Else Exit Sub End if End if Now the user has a choice. If he or she places a disk in the drive and clicks on the Yes button, the process can continue. If the No button is clicked, Visual Basic will exit the sub-routine. If a disk has not been formatted, you may want to give the user the option to format that disk. If a disk is full, you can let the user insert a new disk. Always be aware of the problems that can occur, especially because of user interaction, and code accordingly. Using the On Error Statement Inline Error Handling Error-Handling Routines Trappable Errors

Using The Error-Handling Hierarchy


Unless you code error-handling routines in every procedure you write, Visual Basic needs a way of finding that error handler when a runtime error occurs. This section discusses the methods by which Visual Basic finds that error-handling procedure and what happens if one is not found. Developers of Visual Basic programs control error handling with the On Error statement. The On Error statement dictates how a runtime error will be handled when encountered. If a procedure in your code is executing and an error occurs, Visual Basic looks for On Error within that procedure. If the On Error statement is found, the commands following On Error are executed. If an error handling does not exist in the procedure in which the error occurred, Visual Basic goes up in the calling chain to the code that called the current procedure to look for an On Error statement. Visual Basic continues going up the calling chain until some kind of error handling is found. If there is no procedure in the calling chain with error handling, the application ends with a fatal error. The following example shows how errors will be handled in a simple code example: Private Sub Main() Call SubroutineA End Sub Private Sub SubroutineA() Call SubroutineB End Sub Private Sub SubroutineB() Dim I as Integer Dim J as Integer I=0 J = 10 / I End Sub

95

If you have these three procedures in your application, you can see that this code will generate a runtime error in SubroutineB. The statement J = 10 / I will generate a "divide by zero" error. When this application runs, sub-routine Main starts, and then it calls SubroutineA, which in turn calls SubroutineB. After the runtime error occurs in SubroutineB, Visual Basic must determine how to handle the error. In this case, when the error occurs in SubroutineB, Visual Basic looks in SubroutineB for error handling specified by an On Error statement. Because it is not coded, Visual Basic goes up the calling chain to SubroutineA and checks there for error handling. Again because none is found, Visual Basic goes up the chain to Main. Because Main does not have any error handling either, a fatal error occurs and the application shuts down. Now consider the case where sub-routine Main is modified to look like this: Private Sub Main() On Error GoTo Main_Error Call SubroutineA Exit Sub Main_Error: Msgbox "An error occurred." Resume Next End Sub When this code runs, a division by 0 still occurs in SubroutineB. Visual Basic still checks for error handling in SubroutineB and then in SubroutineA. When it is not found, Visual Basic checks Main for an On Error statement. This time it finds one and does whatever the errorhandling statement instructs. In this case there is a statement instructing that execution continue at the label Main_Error. A message box is displayed, and then a Resume Next statement is encountered. You must be aware of the point that a Resume Next or a Resume statement will send you to after the error occurs. A Resume Next statement will not send you back into SubroutineB to the statement after the divide by 0. It sends you instead to the statement in Sub Main after the line that made the call to the procedure that generated the error. In this case the Exit Sub statement would be executed next. It is important to remember two important things about the errorhandling hierarchy. The first is that Visual Basic will search up the procedure in the calling chain until an error handler is found. As an example look at a case where there are three procedures in the calling chain: ProcA, ProcB, and ProcC. ProcA calls ProcB, which in turn calls ProcC. If an error occurs in ProcC, Visual Basic will look in that procedure for an error handler. If one is not found, Visual Basic will go up the calling chain to ProcB and look for an error handler there. Again if one is not found, Visual Basic will check ProcA. The second thing to remember is that if there is no error handler in the calling chain, a fatal error occurs and the application shuts down.

Common Error-Handling Routines


Sometimes while you are creating an application with error-handling routines in procedures, you may find yourself writing the same code over and over again. All your error-handling routines may be anticipating the same error numbers. Instead of duplicating the same error-handling code in many procedures, you can create a common error-handling function and call it from the individual errorhandling routines within your other procedures. Assume, for example, that you are developing an application that does extensive file processing. You find that many of your procedures check for errors 53 (File Not Found), 58 (File Already Exists), and 61 (Disk Full). Without a common error-handling routine, you might find yourself coding the following in every procedure that references files: fErrorHandler:

96

Dim msg As String Select Case Err.Number Case 53 ' file not found msg = "File not found. Do you want to try again?" If MsgBox(msg, vbYesNo) = vbYes Then Resume Else Exit Function End If Case 58 ' file already exists msg = "The file already exists. Do you wish to overlay?" If MsgBox(msg, vbYesNo) = vbYes Then Resume Next Else Exit Function End If Case 61 ' disk full MsgBox "The disk you specified is full. The operation cannot continue." End End Select Your error handlers can be even longer, trapping additional errors that your users might encounter. Instead of repeating the same code many times in different procedures, you should move your error-handling code to a common errorhandling function. You may find yourself not only using the common function many times within a project, but also find yourself including the common error handling in many projects you write. For this reason it is best to put your common error-handling function in a separate standard module so it can be easily ported to other projects. When you create a common error-handling function, consider the following: A common error-handling routine needs to know what error occurred. The easiest way to pass this information is by passing the Err object to the function. You need to know the results of the error handling. Will execution continue? Should the line of code that caused the exception be bypassed or run again? This information can be passed back as the return value or as an argument of the function. The error-handling function or module may be used in other projects. Try not to reference forms or procedures specific to one project. This will prevent rewrites later. Code the function to handle as many trappable errors as you can. The more types of errors you trap for the more specific and user-friendly your error handling can be. Even if you do not code for all the trappable errors, you can add to the function at a later time. Include handling of any errors for which you do not have specific error handling. For example, your error handler may trap for errors 53, 58, and 61. Include some code to handle any other errors. Unexpected errors always occur in applications. In addition future releases of Visual Basic may include more trappable errors than the current release. When you create an error-handling function, you must decide how to tell a calling procedure what should be done after the error handling has completed. Do you want the code to retry (Resume), bypass the code (Resume Next), or shut down the application? One way to do this is to pass back the information through the return code of the function by using constants: Public Const iRESUME = 1 Public Const iRESUME_NEXT = 2 Public Const iEXIT_PROCEDURE = 3 Public Const iEXIT_PROGRAM = 4

97

Remember to include the constant definitions in the BAS module that contains the error-handling function. The error code that you previously had in many procedures throughout your application can now be combined into one error-handling function: Public Function fErrorHandler(objError As Object) As Integer Dim msg As String Select Case objError.Number Case 53 ' file not found msg = "File not found. Do you want to try again?" If MsgBox(msg, vbYesNo) = vbYes Then fErrorHandler = iRESUME Else fErrorHandler = iEXIT_PROCEDURE End If Case 58 ' file already exists msg = "The file already exists. Do you wish to overlay?" If MsgBox(msg, vbYesNo) = vbYes Then fErrorHandler = iRESUME_NEXT Else fErrorHandler = iEXIT_PROCEDURE End If Case 61 ' disk full MsgBox "The disk you specified is full. The operation cannot continue." fErrorHandler = iEXIT_PROGRAM Case Else ' covers all other errors msg = objError.Number & "=" & objError.Description & _ vbCrLf & "Source = " & objError.Source MsgBox msg fErrorHandler = iEXIT_PROGRAM End Select End Function Now that your error handling has been centralized in a common function, the error handling in individual procedures can be simplified to something that looks like this: sub1_error: Select Case fErrorHandler(Err) Case iRESUME Resume Case iRESUME_NEXT Resume Next Case iEXIT_PROCEDURE Exit Sub Case iEXIT_PROGRAM End End Select For most of your procedures, the preceding code will be sufficient for handling errors. The common error function will be called, and the way of handling any given error will be returned. There are always times, however, where you will

98

customize the error handling in different sub-routines and functions to handle unique situations. For these cases it is best not to alter a common error-handling function.

Using The Error Function


A function related to errors and error processing is the Error function. The Error function has one argumentan error number. It returns a string description of the error corresponding to that number. For example, if you coded the following: Msgbox Error(61) the user would see a message box with the error description Disk full, as shown in Figure 11.5.

FIGURE 11.5 An error message from the Error function. If the error number is not specified, the description of the most recent runtime error is displayed (the same value as Err.Description). If there have been no runtime errors, the result is a 0 length string. If the error number is valid but has no Visual Basic definition, the return value will be Application-Defined or ObjectDefined Error. An error will occur if the error number is not valid.

Using The Error Statement


The Error statement can be used to simulate or force errors to occur. The syntax is as follows: Error <error number> For example, Error 51 generates an Internal Error. The Error statement is available in Visual Basic 6 for compatibility with older versions. With Version 6, you should use the Raise method of the Err object.

Inline Error Handling


Another form of the On Error statement is On Error Resume Next. This alternative is used to handle an error immediately after the line causing the error rather than branching to a specified error handler. Another use of On Error Resume Next is to simply allow your application to ignore errors that you don't need to handle at all. In Listing 11.1, you can assume that the GetObject function might cause a runtime error. Notice that this code attempts to handle the error on the following line by first evaluating the current value of Err.Number. LISTING 11.1 THE ON ERROR RESUME NEXT STATEMENT ALLOWS US TO HANDLE ERRORS AS THEY OCCUR

99

Private Sub XLInLine() Dim errTemp As Long On Error Resume Next Set xl = GetObject(, "Excel.Application") Select Case Err.Number Case 0 MsgBox "Successfully Opened Excel" Case 429 Set xl = CreateObject("Excel.Application") Case Else MsgBox "Fatal error #" & Err.Number _ & " (" & Err.Description & ")" & _ ": Passing the buck" End End Select Err.Clear End Sub The example also checks to see if the value of Err.Number is 0, which indicates that no error has occurred. Notice that the Err object's Clear method is called after the Select Case structure handles the error. You need to call Err.Clear because inline error handling doesn't use the Resume statement, and Resume is what you normally rely on to reset the Err object. If an error had occurred in the code in Listing 11.1 and Err.Clear hadn't been used, the Err object would continue to store information about this error. This could mislead a called procedure, which might have its own error handlers and would depend on the value of Err.Number to function properly. Remember to call Err.Clear after each place where, in a routine where you handle inline errors, an error could occur in any of these locations, and you must handle each one individually. Generally, you'll want to be careful about using inline error handling. Your procedures can get quite long and more difficult to construct and maintain since an error handler must follow each line that could cause an error. As we mentioned previously, the On Error Resume Next statement is used for inline error handling. There are other times, however, when the On Error Resume Next statement comes in handy. There are basically two uses for On Error Resume Next: Ignore errors and keep on processing. This is useful when you might expect an error to occur occasionally, but the error does not affect the remaining code. For example, the code in Listing 11.2 will process all the controls on the current form. LISTING 11.2 USING ON ERROR RESUME NEXT TO IGNORE UNIMPORTANT ERRORS On Error Resume Next For each ctrlCurr In Controls ctrlCurr.Text = UCase(ctrlCurr.Text) Next ctrlCurr Whenever the loop hits a control without a Text property, such as a Label, VB generates a runtime error. In Listing 11.2, the error is not relevant and does not require handling because you want VB to simply ignore any control that does not apply (controls without a text property). The On Error Resume Next statement tells the system to ignore the error and keep going. Process errors immediately after they occur. This is possible because VB sets the values of Err.Number and Err.Description when an error occurs even if On Error Resume Next is in effect. This enables you to use a second style of local error trapping inline error handlingas previously discussed in this section. Inline error handling therefore offers you an alternative, and often less cumbersome, way to process errors in a routine.

100

Chapter 12 - Creating A COM Component That Implements Business Rules Or Logic


A COM component provides reusable, encapsulated functionality to client programs running in a Windows environment. You can use VB to create one of the several types of COM components. A COM component's threading model affects the way that the COM component handles requests from clients and manages memory. An object is externally creatable when an application that can serve as an ActiveX client can declare an instance of (that is, instantiate) that object as a variable and then manipulate the methods and properties of that object. You can register your server component in the Windows Registry in one of several ways. These ways are discussed in more detail in the section titled "Registering and Unregistering a COM Component." A Visual Basic project provides an externally creatable object through certain project settings and by possessing a class module whose properties you have set appropriately. An externally creatable object also typically provides a gateway to other objects that can't be directly created by clients. Every COM component provides at least one externally creatable object. The server component's externally creatable objects, together with the other objects indirectly exposed by the externally creatable objects, is known as an object hierarchy or object model. You can implement your own COM component's object model with custom object classes. Although you can create custom object classes specific to a single application, Microsoft's published documentation and courseware are full of examples of classes used to implement COM components.

Contents
1. 2. Overview of COM Component Programming The COM Specification and the ActiveX Standard Comparing In-Process and Out-of-Process Server Components Steps in Creating a COM Component Implementing Business Rules With COM Components Implementing an Object Model With a COM Component Implementing COM Components Through Class Modules The Uses of Class Modules Starting a Class Module in a Standard EXE Project The Class Module Name Property Implementing Custom Methods in Class Modules Implementing Custom Properties in Class Modules Implementing Custom Events in Class Modules Built-In Events of Class Modules Using Public, Private, and Friend Keywords Storing Multiple Instances of an Object in a Collection Implementing Built-In Collection Features in the Dependent Collection Class Declaring and Using a Class Module Object in Your Application Manipulating Class Object Properties and Methods Managing Threads in a COM Component Managing Threads in ActiveX Controls and In-Process Components Managing Threading in Out-of-Process Components The Instancing Property of COM Component Classes Using Private Instancing for Service Classes Using PublicNotCreatable Instancing for Dependent Classes Instancing Property Settings for Externally Creatable Classes Using SingleUse Instancing for Separate Instances of Every Object

3. 4. 5.

6.

7.

101

Using GlobalSingleUse to Avoid Explicit Object Creation Using MultiUse Instancing for Object Sharing and Using GlobalMultiUse Instancing to Avoid Explicit Object Creation Deciding Between SingleUse and MultiUse Server Classes 8. Handling Errors in the Server and the Client Passing a Result Code to the Client Raising an Error to Pass Back to the Client 9. Managing Components With Visual Component Manager Storing VCM Information in Repository Databases Making VCM Available in the VB IDE Publishing Components With VCM Finding and Reusing Components With VCM 10. Using Interfaces to Implement Polymorphism Steps to Implement an Interface Class Creating the Interface Class Implementing the Interface Class in Other Classes Methods for Using the Interface Class in a Client Method A: Using an Object Variable in the Client Method B: Using Wrapper Routines in the Client 11. Providing Asynchronous Callbacks Providing an Interface for the Callback Object Implementing the Callback Object in the Client Manipulating the Callback Object in the Server 12. Registering and Unregistering a COM Component Registering/Unregistering an Out-of-Process Component Registering/Unregistering an In-Process Component 13. Sending Messages to the User from a COM Component Managing Forms in an Out-Of-Process Server Component Managing Forms in an In-Process Server Component 14. Choosing the Right COM Component Type 15. Implementing Scalability Through Instancing and Threading Models 16. Under-the-Hood Information About COM Components

Overview Of COM Component Programming


Recall the syntax you saw in the preceding chapter for instantiating ActiveX objects in a client application: Dim xl as Excel.Application and set xl = CreateObject("Excel.Application") or set xl = GetObject(,"Excel.Application") Some server components (including any server components that you create with VB) support Microsoft's recommended standard, Dim As New. Assuming you had created a server component MyServer with a class MyClass, you could set up an object variable reference to the server component class with the line: Dim objSvr As New MyServer.MyClass This is the only line of code you would need, because the As New keyword declares and instantiates the object variable in a single line of code.

102

A somewhat more resource-efficient way to use the New keyword would be to declare the object variable without the New keyword, only using New when you're ready to use the object in code: Dim objSvr As MyServer.MyClass 'Later in code: Set objSvr = New MyServer.MyClass This means that the resources to instantiate the object would not be required until the time that the object was actually needed. The common thread in each of these sample lines is the servername. classname syntax you use when referring to your server component class. The preceding statements that reference the system name of the server component and its class (Excel.Application or MyServer.MyClass) assume that you have already set a reference to the server component in the client project. In this chapter, you see how to create, test, and maintain server components that can behave as Excel.Application did in the examples from Chapter 13.

The COM Specification And The ActiveX Standard


Microsoft now uses the term ActiveX as the name for its standard for enabling objects to be instantiated and communicate with each other in a Windows environment. ActiveX is an extension of the older OLE standard. The main difference between ActiveX and OLE standards is that Microsoft markets ActiveX as a technology for providing Internet-enabled, docucentric computing solutions. You will still see the term OLE used in Microsoft documentation and in the names of many of its internal object types and events. This is because the OLE standard is still one part of ActiveX, the part that provides object linking and embedding. In fact, OLE stands for "object linking and embedding." ActiveX and OLE, in turn, are based on a more generalized standard or specification known as the Component Object Model, or COM. The COM specification lays out the general blueprint for any object standard and defines how separate object components can communicate with and manipulate each other.

Comparing In-Process And Out-Of-Process Server Components


VB6 enables you to create two types of COM components: Out-of-process server components. Out-of-process server components are always EXE files. In-process server components. In-process server components are always DLL files.

The difference between these server component types is with respect to the client application. When a server component runs out-of-process with respect to its client, it has the following characteristics: It can run as a standalone application completely apart from the client. It does not share the same address space under the operating system. Its public creatable classes can be instantiated either as SingleUse or MultiUse objects. (See "The Instancing Property of COM Component Classes" later in this chapter for a discussion of these concepts.) When a server component runs in-process with its client, it has these important features: The server component and client share the same executable space under the operating system. The server component and client share some of the same memory.

103

An in-process server component's public creatable classes can be instantiated only as MultiUse objects. (See "The Instancing Property of COM Component Classes" later in this chapter.) As you might imagine, there are pros and cons to both in-process and out-of-process server components.

Performance: In-Process Server Components Win the Contest


Because an in-process server component shares the same process space with its client at runtime, communication between the in-process server component and its client can avoid the ActiveX interface. The client can therefore call the in-process server component very efficiently.

Flexibility and Availability: Out-of-Process Server Components Win the Contest


Here is a list of things only an out-of-process server component can do: Provide classes that are either SingleUse or MultiUse. (See "The Instancing Property of COM Component Classes" later in this chapter.) Display modeless forms. Use the End statement (although not recommended in every application because it prevents the firing of an Unload or Terminate event).

Steps In Creating A COM Component


To implement a server component application, you must define classes that will become the server component's exposed objects. n this section, you learn the basic steps for creating a server component and implementing classes in the server component. Later in this chapter, you also see how to test, fine-tune, maintain, and distribute your server component. To create a server component application, follow these basic steps: 1. Start a new project for the server component class. Use the ActiveX DLL template for in-process component or EXE template for out-of-process component. 2. Change the project's name to match the name you want to give your server component application. This choice of name is important, because it is the name that the Windows Registry will use to identify the server component and the name that clients will use when they instantiate objects from the server component with the servername.classname syntax. For each object class that you want to implement in the server component, follow these steps: 1. Add a class module to the server component project. 2. Set the class module's Name property to match the name you want to give the class. 3. If the class is to be externally creatable, make sure that the class module's Instancing property is set to SingleUse, MultiUse, GlobalSingleUse, or GlobalMultiUse. (The ActiveX DLL or ActiveX EXE automatically sets this property, but you should check it anyway.) If the class should not be directly created by clients, choose one of the other Instancing property's options (see the following section titled "The Instancing Property of COM Component Classes"). 4. Implement the class object's members (methods, properties, and events) according to the guidelines given in the sections under "Implementing COM Components Through Class Modules" later in this chapter. 5. Create the other classes you need to round out your object's functionality.

Implementing Business Rules With COM Components

104

In a multitier business solution, the most common three tiers are as follows: User-interface tier Business-rules or business-logic tier Data-access tier

The user-interface tier typically resides on each user's workstation; the business-rules or business-logic and data-access tiers more often reside on a network server. You can use ActiveX with VB to create COM components that implement business logic in the solutions that you develop. The objects provided by COM components to implement business logic are often known as business objects. You might, for example, provide a business object to the system in the form of a COM component that gives credit information and performs credit validation for customers. When designing your system, you need to determine where components for the business-logic tier should reside and what the best form of implementation should be. The first question to ask when designing a business-logic tier is this: "How should the tier be implemented?" Some possibilities might include the following: Implement as part of the user interface (compiled into that tier's executable). Although this might be the easiest to program in the beginning, it would quickly become a maintenance nightmare. Every time that your company changes the way it does business, you would need to make a change to the application and then distribute it to all the users. An email with a note to please rerun the install would probably not be sufficientif a business rule changes, it should change for all users at the same time; it would be hard to count on all users to implement the change at, say, 12:00 a.m. on a particular date. Implement as part of the system's data structure (a trigger or a stored procedure). This would eliminate the problem of having to update every user's workstation. Such a choice will almost certainly confuse the functions of any data tiers and the business- logic tier, however. The business-logic tier would not be a separate unit and so couldn't be easily separated from the data when needed. This solution also impedes scalability. What would happen, for instance, if the data itself were someday split into more than one database? Business rules would have to be distributed among those databases. If any business rules were duplicated between the databases, they would have to be maintained in two places. Implement as a COM component on a server. This solution would provide the most long-term stability for maintainability and scalability. It would guarantee that future changes to the business rules would only require maintenance in one place. It would also guarantee that upward scaling of the system to accommodate more users or increased resources for reasons unrelated to business rule changes would not require changes in the component or its implementation.

Implementing An Object Model With A COM Component


To implement an object model in a COM component, you typically would use one or more classes of objects. Define the major real-world objects that the component needs to model. These major objects will become the classes of the COM component. Define the attributes and behaviors of these real-world objects. The attributes will become properties of the classes and the behaviors will become methods or events. Identify any repeating groups of objects from among the attributes of the classthat is, groups of subobjects that can belong to the class and whose number might vary. An employee can have one or more previous jobs, for example, so PrevJobs could be an attribute of an Employee class. Such repeating groups of objects can be implemented in Collection objects, which in turn contain zero or more individual members known as Dependent objects.

105

An Employee class in a Human Resources COM component might have properties such as HireDate, Salary, and VacationTimeAccrued; methods such as Promote, TakeVacationTime, Hire, and Fire; and events such as VacationAccrualChange.

Implementing COM Components Through Class Modules


To create COM components in VB, you must first know how to create and manipulate a class module. This section and the following subsections discuss the basics of VB class modules and show how to use objects created from classes in a standalone (non-ActiveX) project. The information in these sections is referenced again during the VB ActiveX programming discussion in later sections. The Uses of Class Modules

The Uses Of Class Modules


You use class modules to implement your own custom object variable types in VB. Just as VB provides the programmer with TextBox, Form, CommandButton, or RecordSet object types, so you can provide other programmers (or yourself ) with, say, Employee, FileLocation, or other object types. And just as the predefined object types have their particular properties, events, and methods, so can you define custom properties, events, and methods for your custom objects. Each custom object type that you wish to define must have a class module behind it. You can use class modules in two different environments: As part of an ActiveX component server. As part of a server, your class gets registered in the Windows Registry and other applications can create object instances of it. As part of a standard EXE. Code in the rest of the application outside the class module can declare and manipulate object variables based on the class modules you provide within the application. The following subsections concentrate on the basics of creating and manipulating a class in a standard EXE. Later sections extend the use of classes to the world of COM components. Starting a Class Module in a Standard EXE Project The Class Module Name Property Implementing Custom Methods in Class Modules Implementing Custom Properties in Class Modules Implementing Custom Events in Class Modules Built-In Events of Class Modules Using Public, Private, and Friend Storing Multiple Instances of an Object in a Collection Implementing Built-In Collection Features in the Dependent Collection Class Declaring and Using a Class Module Object in Your Application

Starting A Class Module In A Standard EXE Project


You can add a Class module to your project by choosing the Project, Add Class Module VB menu option. From the resulting dialog box (see Figure 12.1), you can choose Class Module, VB Class Builder, Complex Data Consumer, Data Source, or OLE DB Provider.

106

FIGURE 12.1 Dialog box for adding a new class module to your project. If you choose the Class Module icon, you will find yourself in the new class module's code window.

The Class Module Name Property


Your class module's Name property is important because it is the name that controller code will use when it instantiates a copy of your class as an object. You can change the class name from the Project Explorer by following these steps: In Project Explorer, right-click the name of the class module to bring up the pop-up menu for this class module (see Figure 12.2).

FIGURE 12.2. The pop-up menu of the class module Choose the Properties option from the pop-up menu. The only property listed is the Name property, and you can change it here (see Figure 12.3).

107

FIGURE 12.3. The properties dialog box of a class module in a standard EXE project.
NOTE - Changing the Name of a Class That Is Already in Use: Be careful when you change the name of a class that is already in use, because controller code depends on the name of the class to instantiate objects.

Implementing Custom Methods In Class Modules


When you create methods in a class, you can call them in the rest of your code just as you would call methods for any standard VB class. To provide methods for your class, just create Public procedures in the class module (both Sub and Function procedures will work). Code from other parts of the application or in ActiveX controllers can then call these procedures directly as methods of the object Class. The example in Listing 12.1 defines a Public Sub procedure SearchPath in the class module FileFind. Code residing elsewhere in the application can call the SearchPath method of the object Class. The calling code passes to the method the name of the directory to search and a file specification to search for. LISTING 12.1 IMPLEMENTING A METHOD WITH A Public PROCEDURE [In the Class module] Public Sub SearchPath(strDirToSearch As String, _ strFileSpec As String) 'Add a backslash to the directory 'name if one's not there already If Right(strDirToSearch, 1) <> "\" Then strDirToSearch = strDirToSearch & "\" End If 'Get the name of the first file in the 'directory fitting the specified pattern Dim strName As String strName = Dir$(strDirToSearch & strFileSpec) 'Loop through all file names in the directory 'fitting the specified pattern Do Until strName = "" 'Add this file to our list

108

frmFiles.lstFiles.AddItem strName 'get the next file name in the list strName = Dir$ Loop End Sub [In the General Declarations of a Form] Private FF As FileFind [In the calling routine] Private Sub cmdFindFiles_Click() lstFiles.Clear Set FF = New FileFind FF.SearchPath _ dirFind.Path, _ txtFileSpec.Text End Sub

Implementing Custom Properties In Class Modules


You can create custom properties as part of the functionality that your Class object provides to calling code. You can implement these custom properties either as Public variables of the Class or by using special Property Let/Set and Property Get procedures.

Implementing Properties as Public Variables


A Public variable in a class module automatically implements a property of the class. You can declare Public variables in a class module in the same way you declare Public variables for other modules such as forms and standard (BAS) modules. To add a property called "Name" to a class, you just write the following lines in the General Declarations section of a class module: Public Name as String This is, of course, the same way that forms use Public variables. Therefore, when calling code accesses your class by instantiating an object, the caller can manipulate the properties of the object it has declared with the standard object.property syntax, as in the following line: objMine.Name = "Geryon" and later MsgBox objMine.Name The basic advantage of implementing an object property with a Public variable is that it is very easy to do. The downside is that You can't create a read-only property, because Public variables are always writable. You have no way of triggering an automatic action when the property is manipulated (such as hiding an object when a Visible property is set to False). Different simultaneous instances of a class share the same Public variables, and thus cause all sorts of possible confusion in your calling code. To get around these disadvantages, you must give up the idea of using Public variables to implement class properties and instead use Property Let/Set and Property Get procedures as described in the following sections.

109

Implementing Properties with Property Procedures


With some additional work, you can overcome the disadvantages of properties implemented as Public variables. You can use the special Property procedures instead. To implement the Name property of a class with Get and Let procedures, you put the code shown in Listing 12.2 in your class module rather than a Public variable declaration. LISTING 12.2 IMPLEMENTING A CLASS PROPERTY WITH Property Get AND Let PROCEDURES [General Declarations section of Class] Private gstrName As String [General Code section of Class] Public Property Let Name(strVal As String) gstrName = strVal End Property Public Property Get Name() As String Name = gstrName End Property

> Using a Private Variable of the Class to Store a Property


Notice in Listing 12.2 that you still declare a variable in the class's General Declarations section as you would when implementing properties with Public variables, but this time the variable is a Private variable. This means the variable won't be visible outside the class. You use it to store the value of the property you are implementing, but code won't access this variable directly. Instead, Property Let and Get procedures will be the gateway to the property.

> Allowing Writes to a Property with Property Let


The Property Let procedure is a Sub procedure that will run whenever the controlling code attempts to assign a value to the property, as when, say, it runs code such as: objMine.Name = "Ciacco" Notice that the Property Let procedure in Listing 12.2 takes a single parameter. The parameter holds the value that the controlling code is trying to assign to the property. If your controlling code had run the single line listed here, the value of the parameter strVal in the Property Let procedure would be "Ciacco". The Property Let in Listing 12.2 then does what Property Let procedures almost always do: It stores the incoming parameter value in the Private variable (gstrName in this case) where the value of the Name property is being held.

> Allowing Reads of a Property with Property Get


Property Get is a Function procedure that will run whenever controlling code attempts to read the value of the property named by the procedure, as in either of the two lines in Listing 12.3. Notice that the Property Get in Listing 12.2 takes no parameters, but has a return type and sets a return value equal to the name of the Property Get procedure, just as any normal Function procedure does. That is because the Property Get's job is to pass a value back when controlling code requests it. Typically, the Property Get will do just as the example in Listing 12.2 does: It will get the value of the property from the value stored in a Private variable of the class. LISTING 12.3 CONTROLLER CODE THAT READS THE VALUE OF AN OBJECT'S PROPERTY

110

ThisName = objMine.Name MsgBox "And then we met " & objMine.Name

> Implementing a Read-Only Property


If you would like a property to be read-only for controller code, all you have to do is leave out the Property Let procedure from your class. That way, the controller has no way of writing the property. You can still store the value of the property in a Private variable of the class module. Other parts of your server class can manipulate the property by changing the Private variable, but the controller application can't change it directly. Listing 12.4 implements an Integer-type property to track the number of times the Name property changes. The tracking property is called Accesses, and is stored in a class Private variable called giAccesses. The Accesses property would be updated indirectly through giAccesses every time the Name property gets changed by a controller (the Property Let Name procedure increments giAccesses). Because there is no written Property Let Accesses procedure, however, there is no way for controlling code to change the Accesses property directly. LISTING 12.4 IMPLEMENTING A READ-ONLY CLASS PROPERTY BY OMITTING ITS PROPERTY LET PROCEDURE [General Declarations of Class] Private giAccesses As Integer Private gstrName As String [Code section of Class] Property Get Accesses() As Integer Accesses = giAccesses End Property Public Property Let Name(strVal As String) gstrName = strVal giAccesses = giAccesses + 1 End Property Public Property Get Name() As String Name = gstrName End Property
WARNING - Don't Use Public Variables to Store Intermediate Property Values: Don't use class Public variables to store Property Let/Get's intermediate values, because then controller code can read and write them directly, which will confuse the issue of how to access the property's value. Always use class Private variables to store the values of properties that you implement with Property procedures.

Implementing Custom Events In Class Modules


You can define and fire your own custom-defined events in a class module. The act of firing an event from within the class module's code is known as raising the event. When an object instantiated from your class raises an event, the controller code can handle that event in an event procedure.

111

Declaring the Event


You must first define the event by declaring it in the General Declarations section of the class. The syntax for an event declaration is Public Event EventName (argument list) where argument list is a list of arguments declared by name and type similar to the parameter list you would declare in a regular procedure. You might declare an event named FileFound, for example, with the following line in a class's General Declarations section: Public Event FileFound _ (FileName as String, _ FilePath As String) The controlling code would then be able to write an event procedure that received two String-type parameters. As the following section shows, it is the responsibility of the code in the class that raises the event to determine which values the arguments will hold. Unless you specify otherwise with the ByVal keyword, the arguments are passed by reference. This means that the event procedure in the controller code could change the value of the parameters. Modifiable arguments provide a way for an object and its controller to communicate with each other during processing, because you could specify arguments whose purpose is to carry a message back from the controller. In the following example, a third Boolean parameter is added: Cancel. The controller might change this to tell our class object to stop processing: Public Event FileFound _ (FileName as String, _ FilePath As String, _ Cancel as Boolean)

Raising the Event and Implementing Callbacks in a Class Object


The only other thing you need to do in the class to implement an event is to fire, or raise, the event in a single line of code, typically in a method of the class, as shown in Listing 12.5 with the RaiseEvent keyword. Although, as just stated, you raise the event with a single line of code, you will probably need to write several more lines to fully implement the firing of the event, especially if the event provides arguments to the controller: Before firing the event, you will want to prepare the value of the arguments to be passed to the controller, and after the event fires, you will want to check the arguments' values to see whether the controller has changed any of them. Notice that the code checks the value of the Cancel argument in Listing 12.5 after the event fires to see whether the controller wants this code in our class to continue processing. Because controllers can modify an event's arguments, this effectively implements callback functionality between controller and object. LISTING 12.5 RAISING A CUSTOM EVENT FROM THE CODE IN YOUR CLASS '. . .code to assign values of strName and strDir 'initialize value for 'Cancel argument blnCancel = False 'fire the event RaiseEvent FileFound(strName, strDir, blnCancel) 'check to see whether controller

112

'changed the Cancel argument If blnCancel Then Exit Sub

Built-In Events Of Class Modules


Every Class module comes with two predefined events: Initialize and Terminate. You can find the event procedure stubs for these two events in the class module's code window by clicking the Objects drop-down list (left-hand list box at the top of the code window) and choosing the Class object. The Procedures drop-down list (right-hand list box at the top of the code window) then displays the Initialize and Terminate event procedure stubs, which you may navigate to with the mouse (see Figure 12.4).

FIGURE 12.4 Navigating to a class module's Initialize and Terminate event procedures.

The Initialize Event


The Initialize event happens whenever a routine in another part of the application or in an ActiveX client uses your class module to instantiate a new copy of an object. You therefore want to put code in the Initialize event procedure that is appropriate forwellinitialization. You might set default initial values of properties from the Windows Registry or Private constants, for instance. If your class accesses data, you might open database files here. The example in Listing 12.6 initializes a classwide Private variable that is used as the storage for a pair of Property Let/Property Get procedures. LISTING 12.6 INITIALIZING A PROPERTY'S VALUE IN THE CLASS MODULE'S INITIALIZE EVENT PROCEDURE Private Sub Class_Initialize() iNumberOfAccesses = 0 End Sub

113

You should remember that the Initialize event takes place before your object is completely instantiated. Therefore, it is not a good place to do things such as instantiating another object variable of the same class: This would cause an infinite recursive loop, as successive copies of the object would try to instantiate each other.

The Terminate Event


The Terminate event happens whenever a routine in another part of the application or in an ActiveX client destroys the object instance of your class module. An object gets destroyed in one of two ways: The variable that was used to hold your class module's object goes out of scope in the calling code. The calling code explicitly destroys the object with the syntax Set Object = Nothing. You might use the Terminate event procedure to close data files or write information to the Windows Registry or INI files, or just to give a farewell message to your user.

Using Public, Private, And Friend Keywords


If a property or method is added to a class module, it becomes part of the class. This property or method is usable by other modules in the same project as the class module. It is also available to other projects, as a property or method of objects derived from that class. This is the default setting for the scope of a property or method. There are three other ways of defining the scope of a property or method: Public Private Friend

Using the Public Keyword


If the Public keyword is used when defining a property or method, the property behaves as though the Public keyword is not used. This is because the default scope of a property or method defined in a class module is Public. Public properties and methods are available to other modules in the same project as the class module. They are also available to other projects as properties and methods of an object derived from that class module. Most properties and methods will be defined as Public. Consider the following, for example: Public Property Get HireDate() as Date Public Sub Save()

Using the Private Keyword


Using the Private keyword when defining a property or method creates a property or method private to that class module. Only code in that class module can access the property or method. Private methods are typically support routines used by the properties of a class. For example, you may need some helper routines that perform date calculations. Consider the following, for example:

114

Private Function NextWorkDay(FromDate as Date) As Date Private Sub PrintForm(FormName As String)

Using the Friend Keyword


Using the Friend keyword when defining a property or method creates a property or method private to the project containing the class module. Code in the same class module as the procedure can access the property or method. Code in other modules in the same project can also access the property or method. This is useful when creating helper classes that should not be used by other programs. Friend properties and methods are often used when a number of classes assist the main class that other programs will use. A class used to write another object to a file, for example, might have two methods: Load and Save. These methods, declared as Friend, would allow the calling object to access them. They would not be available to other projects. Consider the following, for example: Friend Sub Load(FileName As String) Friend Function Save(FileName As String) As Boolean By explicitly setting the scope for each of the properties and methods in a class, you can better control how other modules and programs access them. Most properties and methods will be Public; however, having access to the Private and Friend keywords allows for greater control of access.

Storing Multiple Instances Of An Object In A Collection


A controller may need to create an indefinite number of copies of a single object class. The FileFind method might instantiate a special File object for every file it finds, for instance. You don't know, however, exactly how many Files might get created. You have already seen a concept in VB that enables you to implement something like this: the collection. To implement your own custom collection in a project, you need to add two classes: A dependent class, which implements a single object of the collection A dependent collection class, which implements the collection of dependent Class objects

These classes are in addition to at least one other class that probably already exists in your project: the parent class, which is considered to "own" the dependent collection class. By convention, a to dependent class Name is a singular noun and a dependent collection class Name is the plural form of the same noun. You might name a dependent collection class Files and a dependent class File, for example.

Setting Up the Dependent Class


To start a dependent class in your server project, just insert a class module into the project and make sure that the new module is named appropriately. As mentioned earlier, the name of the dependent class should be a singular noun. You can then give the dependent class any features you need individual elements of the collection to have, such as custom properties, methods, or events.

Setting Up the Dependent Collection Class


Once again, insert a class module into the project and make sure that it is named appropriately: Typically, the name will be the plural form of the dependent class name.

115

Now, in the General Declarations section of the newly inserted module, declare a Private Collection variable, for example: Private colFiles As New Collection The Collection type has special methods and properties discussed in the next section that enable you to implement a collection of objects. You declare the Collection in the General Declarations section so that the collection gets initialized as soon as an object is instantiated from this class. You declare it as Private so that controller code can't directly manipulate the Collection object. Instead, the controller code will have to go through wrapper methods (Public procedures) that you create in this class. Implementing Built-In Collection Features in the Dependent Collection Class

Implementing Built-In Collection Features In The Dependent Collection Class


Collection objects have three built-in methods and one built-in property: The Count property, which returns the number of items in the collection The Add method, which adds a new item to the collection and returns a pointer to the new item The Remove method, which removes an item, given its index or unique key The Item method, which returns a pointer to an existing item, given its index or unique key

As mentioned in the preceding section, you don't call these features directly from outside the dependent collection class. Instead, you need to write Public wrapper functions that, of course, become methods of the dependent collection class. These methods then mediate between controller calls to the collection and the collection itself. The following sections list the wrapper methods you must write for each of these features. 1. 2. 3. 4. 5. The Collection's Count Property and Class Wrapper Method The Collection's Add Method and Class Wrapper Method The Collection's Delete Method and Class Wrapper Method The Collection's Item Method and Class Wrapper Method Initializing the Collection in the Parent Class

The Collection's Count Property and Class Wrapper Method


You need to write a Count method in the dependent collection class to wrap the collection's Count property. This method just calls the Private Collection object's Count property and returns its value, as in Listing 12.7. LISTING 12.7 A WRAPPER METHOD FOR THE COLLECTION'S COUNT PROPERTY Public Function Count() As Long Count = colFiles.Count End Function

The Collection's Add Method and Class Wrapper Method


You need to write an Add method in the dependent collection class to wrap the collection's Add method. You will implement this as a Function returning an object variable pointing to the newly added item, as in Listing 12.8. Notice that you pass the wrapper Add method any properties that you want to assign to the new object you will create. You initialize a new object variable whose type is an object of the dependent class. You then assign the properties that were passed into

116

the wrapper and call the built-in Add method of the Collection object to add the newly created object to the Collection and give it a key value. Finally, you set the return value of your wrapper method to point to the newly added item. LISTING 12.8 A Class ADD METHOD TO WRAP THE COLLECTION'S ADD METHOD Public Function Add (ByVal Name As String, _ ByVal Path As String) _ As File 'Initialize new object variable Dim filNew As New File 'assign parameters to its 'properties FilNew.Name = Name FilNew.Path = Path 'Add it to the collection colFiles.Add filNew, Name 'Let the return value of this 'function point to it Set Add = filNew End Function

The Collection's Delete Method and Class Wrapper Method


You need to write a Remove method in the dependent collection class to wrap the collection's Remove method. You will implement this as a Sub procedure that takes an argument indicating the index in the collection of the object to be deleted, as in Listing 12.9. Notice that the parameter is declared as a Variant rather than as an Integer or a Long as you might expect for an Index value. The reason for this is that controlling code can use either a numeric index or a Stringtype key value to look up an item in the collection. LISTING 12.9 A WRAPPER FUNCTION FOR THE COLLECTION'S REMOVE METHOD Public Sub Remove (ByVal Index As Variant) colFiles.Remove Index End Sub

The Collection's Item Method and Class Wrapper Method


The Item method you will write wraps around the collection's builtin Item method, as you can see in Listing 12.10. The method will return a pointer to a particular item in the collection. To do so, it must take an Index parameter, which can be either an Integer array index of a unique key of type String. As with the Remove method, this parameter must be declared as a Variant to accommodate both these possibilities. LISTING 12.10 AN ITEM WRAPPER METHOD FOR THE COLLECTION'S BUILT- IN ITEM METHOD Public Function Item _ (ByVal Index As Variant) _

117

As Variant Set Item = colFiles.Item(Index) End Function

Initializing the Collection in the Parent Class


The Parent class shows us just the tip of the collection's iceberg. It doesn't have to do much to implement this custom object class collection, but it does need to refer to the collection, because it provides the gateway to the collection by being the only externally creatable class. You need to put a statement in the Parent class's General Declarations that initializes a pointer to the dependent collection class. If the dependent collection class were named Files, for example, you would put a line in the Parent class's General Declarations that would read as follows: Public Files As New Files

Declaring And Using A Class Module Object In Your Application


The previous sections of this chapter have discussed how you can implement a custom object's properties, methods, events, and collections in a class module. Now it is time to see how to use a custom object in controlling code. Such code might reside in another module in the same application or in a separate ActiveX client.

Declaring and Instantiating a Class Object


You have two basic options for actually instantiating a Class object: Declare it like any other variable and assign its contents later, or instantiate the object when you declare it.

Declare Now, Instantiate Later


You can declare the custom object somewhere in your code just as you would declare a VB variable of a standard variable type. Use the class name as the variable, as in the following example: Private FF As FileFind Later in your code, you need to use the Set and New keywords to instantiate the object: Set FF = New FileFind

Instantiate When You Declare


You can declare a Class object and instantiate it with a single statement by using the New keyword in the declaration statement: Private FF As New FileFind

Declaring WithEvents
If you plan to program the Class object's event procedures, you must use the WithEvents keyword in the declaration, as in the following example:

118

Private WithEvents FF As FileFind You can't use the New keyword in a WithEvents declaration, so you always have to instantiate these Class objects later, as described above in the section titled, "Instantiate When You Declare".

Manipulating Class Object Properties And Methods


After you have instantiated your Class object, manipulating the object's properties and methods becomes quite straightforward: You use exactly the same techniques and syntax as you would use to manipulate VB's standard objects. You manipulate a Class object's properties with the Object.Property syntax, as in the following example: objMy.Name = "Vergilio" MsgBox objMy.Path You manipulate a Class object's methods with the Object.Method syntax, as in the examples in Listing 12.11. LISTING 12.11 CALLING CUSTOM METHODS IN CONTROLLING CODE 'Method without argument or return value objName.ShowMsg 'Method with argument objName.KillFile "MY.BAK" 'Method with return value strDir = objName.FindFile("VB.EXE")

Handling a Class Event


You handle a Class object's event just as you would handle events for a VB control: by writing code in the predefined stub of its event procedure. To make the object's event procedure available in a form or another class module, you must declare the object variable with the special WithEvents keyword in the General Declarations section, as in the following example: Private WithEvents FF As FileFind where FF will be the name of the object variable, and FileFind is the name of its class. After you insert this declaration in your code, you will be able to see the object variable in the Objects list box at the upperleft corner of the code window of the file (Form or Class) where you put the declaration. When you choose the object from this list, you will then be able to see the object's events in the Events list box at the upper-right corner of the code window. If you use the Events list box to navigate to the event you are interested in, you will find your cursor blinking inside the predefined event procedure stub for that event, just as you would for any control you had placed on a VB form (see Figure 12.5). At this point, all you need to do is to write code in the event procedure, as in Listing 12.12. If the event procedure furnishes parameters, you can use them and, if appropriate, change them. The event procedure in Listing 12.12 takes three parameters. The first two are informational, and the third (Cancel) could be changed to True to tell the object to stop the process that is generating these events (in this case, a file search process). LISTING 12.12 EVENT-HANDLING CODE FOR A CUSTOM OBJECT NAMED FF

119

Private Sub FF_FileFound _ (strFileName As String, _ strFilePath As String, _ Cancel As Boolean) iFilesFound = iFilesFound + 1 If iFilesFound [me] 20 Then 'tell object to stop processing if 'we found the maximum number of files Cancel = True End If End Sub

FIGURE 12.5 Navigating to the event procedure for a custom object's event.
WARNING - Can't Use As New in WithEvents: Declaration When you declare an object using the WithEvents keyword, you can't instantiate it at the same time with the New keyword. You must instantiate the object later in your code.

Managing Threads In A COM Component


VB6 gives programmers some capabilities for managing threads in COM components. A thread, simply defined, is a single path of execution in a computer. In Win16 (Windows for Workgroups and Windows 3.x), every application had one and only one separate thread. In Win32 (Windows 95, Windows NT, and beyond), a single application may use more than one thread. This might be advantageous if the application instantiates multiple objects at the same time. Each object could have its own thread, or the application might have a fixed number of threads to use and could manage its objects across these threads. All VB6 applications use a type of threading known as apartmentmodel threading. When threads run under the apartment model, the object in each thread is unaware of objects in other threads and has its own separate copy of global data.
NOTE - Can't Use WithEvents in a Standard Module: You can'tuse the WithEvents keyword in a standard module.

120

The options for threading are different for COM components, depending on the type of component that you are developing. The following sections discuss these options. Managing Threads in ActiveX Controls and In-Process Components Managing Threading in Out-of-Process Components

Managing Threads In ActiveX Controls And In-Process Components


If your application is an in-process component (ActiveX DLL) or an ActiveX control, the client application will control the creation and management of threads. You may still, however, specify whether your component will allow a single thread or multiple apartment threads. You can specify whether your component supports multiple apartment threading by choosing either Apartment Threaded or Single Threaded from the drop-down box in the Threading Model section under the General tab of the Project, Properties dialog box, as shown in Figure 12.6. Note that the default setting for in-process and control components is Apartment Threaded. Finally, you can check the Unattended Execution box on the General tab of the Project, Properties dialog box (refer again to Figure 12.6) to ensure that your in-process component is thread-safe.

FIGURE 12.6 Specifying the threading model for an inprocess component (ActiveX DLL) or an ActiveX control.

Managing Threading In Out-Of-Process Components


If your application is an out-of-process component (ActiveX EXE), you have three choices for its threading model (refer to Figure 12.6): A Single thread :- When a client makes a request to the component before previous requests have finished processing, the additional request is serialized. A serialized request is placed in a queue behind other requests on the same thread. To make your out-of-process component single-threaded, select the Thread Pool option on the General tab of the Project, Properties dialog box and make sure that the number of threads is one. Typically, developers choose this option when compiling projects that were created in earlier versions of VB. Round-robin thread assignment to a fixed number of threads:-There is an upper limit on the number of threads that the component will support. As additional requests pile up beyond the maximum number of threads, the system moves to the next thread (or cycles back to the first thread if it was already on the least

121

thread), serializing the new request behind other requests on the next thread. To implement round-robin thread assignment, select the Thread Pool option on the General tab of the Project, Properties dialog box, setting the number of threads to a number greater than one. You must also make sure that the project is marked for Unattended Execution. Round-robin threading can actually manage system resources and performance more efficiently than can unlimited threading (discussed next), because round-robin threading limits the amount of server overhead that will be dedicated to a COM component's objects. The main disadvantage of round-robin threading is that it is nondeterministic regarding the exact allocation of objects to threads. In other words, you can neither predict nor discover which objects are assigned to which threads, nor whether threads are load balanced. Load balancing is the art of allocating roughly similar amounts of work to available threads. In other words, it is possible with round-robin threading that one thread might have half a dozen objects serialized, and another thread may have only one object waiting in its queue, or in some cases no object at all. Unlimited threads:- A separate new thread will begin for every new object. To implement unlimited thread assignment, select the Thread per Object option on the General tab of the Project, Properties dialog box. You must also make sure that the project is marked for Unattended Execution. Unlimited threads will, of course, guarantee that every new connection to the out-of-process component gets its very own thread and therefore suffers no blockage. The performance of the system as a whole might degrade with a lot of unlimited thread connections, however, because the server will have to provide resources for each thread. The section titled "Implementing Scalability Through Instancing and Threading Models" discusses these threading options in the light of solution scalability.
NOTE - Single-Threaded ActiveX Controls Cause Problems in Multithreaded Clients: If a client application is multithreaded, a single-threaded ActiveX control can cause problems for the multithreaded client. In fact, if a multithreaded client application is being written in VB, VB will not even allow programmers to use a single-threaded ActiveX control in the project.

The Instancing Property Of COM Component Classes


Class modules in a COM component can be classified according to their relationship to client applications: Service classes - These classes are for internal use by the server itself and are invisible and unusable to clients. Dependent classes - These classes are visible to clients, but clients can only instantiate and manipulate them indirectly through the third type of class, externally creatable classes. (For a further discussion of dependent classes, see the subsections under "Storing Multiple Instances of an Object in a Collection" in this chapter). Externally creatable classes - These classes are visible to clients, and clients can instantiate and manipulate their objects directly. When you create a class module in a COM component, you should set its Instancing property to reflect how the class module will behave with respect to clients. To set the class's Instancing property, follow these steps: In Project Explorer, right-click the name of the class module to bring up the pop-up menu for this class module. Choose the Properties option from the pop-up menu. On the Properties window, you can choose the drop-down list for the Instancing property's settings (see Figure 12.7).

122

FIGURE 12.7 Choices for the Instancing property in the Properties dialog box of a class module in an ActiveX EXE project. Note that the Instancing property is unavailable for the class modules of nonserver projects. In such projects, the Instancing property is always implicitly Private. Using Private Instancing for Service Classes Using PublicNotCreatable Instancing for Dependent Classes Instancing Property Settings for Externally Creatable Classes Using SingleUse Instancing for Separate Instances of Every Object Using GlobalSingleUse to Avoid Explicit Object Creation Using MultiUse Instancing for Object Sharing and Using GlobalMultiUse Instancing to Avoid Explicit Object Creation Deciding Between SingleUse and MultiUse Server Classes

Using Private Instancing For Service Classes


A service class is a class that is only used internally by the server. In other words, you don't want to make this class available in any way to your server's clients, either as an externally creatable class or as a dependent class. If you set the class's Instancing property to Private, no applications outside your server can see this class or instantiate objects from it.

Using PublicNotCreatable Instancing For Dependent Classes


If you want to set up a dependent class, your class must be visible to clients. A dependent class object is, by definition, not directly managed by clients, however. Instead, clients can only instantiate and manipulate dependent classes through externally created classes (see the following sections). For dependent classes, you will set the Class Instancing property to PublicNotCreatable. This signifies that an object of this class is visible to a client, but that the client can't create such an object directly.

Instancing Property Settings For Externally Creatable Classes


As you will recall from the section titled "Overview of COM Component Programming," an externally creatable class provides objects that a client application can instantiate directly. You might guess that, because there is a single Instancing property setting for service classes and one for dependent classes, there would be a single setting as well for externally creatable classes.

123

The situation is a bit more complex than one Instancing setting can handle, however, because external objects have several additional features: External objects can be single use or multiuse. A single-use object can't be shared by more than one client, whereas a multi-use object can (see the following sections for further details). External objects can be global or they can require explicit instantiation (see the following sections for further details). The combination of these two features yields four possible Instancing properties for externally creatable objects, as discussed in the following four sections.

Using SingleUse Instancing For Separate Instances Of Every Object


A SingleUse class can only supply one instance of itself per copy of its component. Assume that your COM component has two SingleUse object classes, ClassA and ClassB. If two clients want to instantiate copies of ClassA from your server at the same time, ActiveX runs a second instance of the server each time an instance of ClassA is created. If one client wants to instantiate a copy of ClassA and another client wants to instantiate a copy of ClassB, however, ActiveX will allow both of them to use the same copy of your server, as long as each object instance is the first requested instance for each class.

Using GlobalSingleUse To Avoid Explicit Object Creation


A Global class does not require a client to explicitly instantiate it before the client tries to use it. In other words, if a Global class exists in a server application that a client is referencing, the client can manipulate the Global class methods and properties as if they were just Public variables and procedures of the client. Suppose, for example, that your server, named MyServices, has a SingleUse server class named FileSvc, with a property named fsSize and methods named fsFind and fsDelete. If client programmers wanted to use the FileSvc class to manipulate a file, they might write code that looks like Listing 12.13. This code declares and instantiates an object from the server/class combination and then calls the object's methods and properties, using the object.member syntax. LISTING 12.13 MANIPULATING AN OBJECT FROM A CLASS THAT'S NOT GLOBAL Dim filCurr As New MyServices.FileSvc filCurr.fsFind "VB6.dep" If filCurr.fsSize = 0 Then filCurr.fsDelete End If You could save client programmers some work, however, by setting the Instancing property of the FileSvc class to GlobalSingleUse. If you did so, client programmers could accomplish the same thing by writing the code in Listing 12.14. Notice that it is not necessary to declare or instantiate an object variable, nor is it necessary to use any sort of object reference at all when you want to manipulate the class's properties and methods. As mentioned in the note accompanying this section, however, you must fully declare the object variable when programming with a GlobalSingleUse class within the component itself. LISTING 12.14 MANIPULATING A GLOBAL CLASS OBJECT

124

fsFind "VB6.dep" If fsSize = 0 Then fsDelete End If Before you decide to make all your externally creatable classes GlobalSingleUse or GlobalMultiUse, however, consider the drawbacks:] Client-side code is now more ambiguous and therefore less maintainable. It's easier to confuse multiple instances of global information. Typically, you will only use GlobalSingleUse when the function a class provides is very, very generic.
Note - Internal Code Must Instantiate GlobalSingleUse Classes Explicitly : Although client projects can access members of GlobalSingleUse classes as if they were Public variables, code within the same server project as the GlobalSingleUse class cannot do so. In other words, if you want to access the members of a GlobalSingleUse class from within the same server project, you must still instantiate an object from the GlobalSingleUse class to do so. The GlobalSingleUse Instancing setting is not available in ActiveX DLL projects

Using MultiUse Instancing For Object Sharing


A MultiUse class can supply more than one instance of itself per copy of its component. A single instance of a COM component can create multiple instances of a MultiUse class. The first request for a class instance starts the COM component. All subsequent requests are serviced by the already running server. If a client application requests several instances of a ClassA, for example, one server creates all instances of ClassA.

Using GlobalMultiUse Instancing To Avoid Explicit Object Creation


All that has been said about GlobalSingleUse Instancing applies to GlobalMultiUse as well. The difference, of course, is that the latter provides a MultiUse object rather than a SingleUse object.

Deciding Between SingleUse And MultiUse Server Classes


If your server is out-of-process (an ActiveX EXE), you have to make the choice between implementing each of its externally creatable classes as a SingleUse or MultiUse object. The main advantage of a MultiUse server is that it uses less system resources because the ActiveX Manager needs to run fewer copies of it. The main reason to use a SingleUse server is to reduce "blocking" of one client's use of a method by another client. Blocking can happen when a server is MultiUse and more than one client is trying to call the same code at the same time. The single copy of the server will attempt to satisfy requests for the same service from more than one client at a time. Because all the instances of a MultiUse server run in a single thread, one client's requests for a service from the server will effectively block all other requests for that service until the request is completed. If you have time-intensivecode in your server and expect several instances of the server to be active at the same time, you can avoid end-user frustration by making the server SingleUse so that clients don't experience delays while they queue up for their turn to use a MultiUse server.

Handling Errors In The Server And The Client


Two applications will be running when the system invokes a COM component: the client application and the server. When an error happens in your server, you want to make sure the error is handled adequately. Because your server should run as transparently as possible to the client, your first line of defense for error handling in the server should be full-blown error handling within the server itself.

125

At times, however, it is more appropriate to let the client handle an error that has occurred. When you need to pass an error back to the client, you can use one of two methods for letting your client application know that an error has occurred: Let the server's methods return a result code to the client showing success or failure. Raise an error code in the server that the system can pass back to the client.

The following sections discuss these two methods. 1. 2. Passing a Result Code to the Client Raising an Error to Pass Back to the Client

Passing A Result Code To The Client


This "soft" method of error handling will not generate an error condition in the client. It will let the controlling application decide whether it is worth checking for an error after calling a method of your server. If the controlling application does check your server method's return value, it can determine whether an error occurred and decide what to do next. To implement this technique, you should write your server methods as functions with an Integer return type. The return type will be, say, zero or some other encouraging-sounding number if no error happens in the function. You need to write and enable an error-trapping routine in the function that will cause the function to return a negative integer or some other equally dire value when an error does occur. The code in Listing 12.15 illustrates both client- and serverside code to implement this solution. LISTING 12.15 RETURN AN ERROR STATUS CODE TO THE CLIENT [Method in the server class] Public Function MyMethod () as Integer 'Initialize return value to OK MyMethod = 0 On Error GoTo MyMethod_Error . . 'do stuff that could possibly cause an error . Exit Function MyMethod_Error: 'return error code MyMethod = Err.Number Resume Next End Function [Code in the client application] Dim iResult as Integer iResult = MyServer.MyMethod() 'Check method's result code for an error If iResult <> 0 then ' do something to handle the error End If 'Or - Following line ignores the method's result code MyServer.MyMethod The client can then check the return value of the server's method to see whether all has gone well. If it gets an error code back from the method, it can then decide what to do.

126

NOTE - Programmer-Defined Error Codes : With this type of error handler, you might consider implementing your own system of error codes. You must use integer values higher than 512, because lower values can conflict with existing ActiveX error codes.

Raising an Error to Pass Back to the Client

Raising An Error To Pass Back To The Client


This method has a more "in-your-face" attitude toward the client. It generates a hard error condition in the client and forces the client to do its own error handling to deal with the server's error. As you can see in Listing 12.16, the method in the server does not have to be a function, because we're not using its return code to signal a success-or-failure status. The method still uses an error handler as it did in the previous technique, but now instead of setting a return value, it uses Err.Raise to cause another error to happen in the error handler. You append the Visual Basic predefined constant vbObjectError to the error code you are raising. This tells the system to pass the error through to the client. When the client receives the error, the value of vbObjectError will be stripped off the error code it sees, and the client will see only the error code's original value. Because an error now occurs in the client, the client must have its own error-handling routine in place. LISTING 12.16 GENERATING AN ERROR IN THE CLIENT [Method in the server class] Public Sub MyMethod2 () On Error GoTo MyMethod2_Error . . 'do stuff that could possibly cause an error . Exit Sub MyMethod2_Error: 'client will see Err.Number as 1000 Err.Raise 1000 + vbObjectErrorlient End Sub [Code in the client application] On Error GoTo MyCall_Error MyServer.MyMethod2 Exit Sub MyCall_Error: ' do something to handle the error Note that Visual Basic's default error-handling strategy at design time is to always break in the class of the server application. To be able to test code that uses the strategy discussed here, you must set the general option known as Error Trapping to Break on Unhandled Errors.

Managing Components With Visual Component Manager


This section and the following subsections discuss how to use Visual Component Manager (VCM). You can use VCM to make it easier to reuse the COM components that you and others create. VCM helps you to perform three basic tasks for component reuse: Publishing components: When you publish a COM component, you make it available to others. Finding components: When you are developing a solution, you investigate whether components that fit your needs already exist.

127

Reusing components: After you have found an existing component, you incorporate it into your project. 1. 2. 3. 4. Storing VCM Information in Repository Databases Making VCM Available in the VB IDE Publishing Components With VCM Finding and Reusing Components With VCM

Storing Visual Component Manager (VCM) Information In Repository Databases


VCM implements the publishing, finding, and reuse of components by maintaining information about them in one or more repository databases. Because VCM enables you to use more than one repository database, you can separate groups of components into different repository databases based on component functionality. VCM also supports repository databases in MS Access format or in SQL Server format. You could use MS Access for local repository databases and SQL Server for repository databases residing on a server. VCM therefore offers a scalable solution to component management. Storing VCM Information in Repository Databases Making VCM Available in the VB IDE Publishing Components With VCM Finding and Reusing Components With VCM

Making Visual Component Manager (VCM) Available In The VB IDE


If you have installed VB6 or any of Visual Studio's other development tools on your system, you have also automatically installed VCM. To make VCM available from the VB IDE, you must also add VCM to the VB toolbar. To add VCM to the VB toolbar, open the Add- Ins menu and make sure that the Visual Component Manager 6.0 item has its Loaded/Unloaded and Load on Startup options checked (see Figure 12.8).

FIGURE 12.8. Using the Add-Ins dialog box to add Visual Component Manager to the VB IDE. After VCM has been enabled, you will see the VCM icon on the VB toolbar (see Figure 12.9).

128

FIGURE 12.9. The Visual Component Manager icon on the VB toolbar. Storing VCM Information in Repository Databases Making VCM Available in the VB IDE Publishing Components With VCM Finding and Reusing Components With VCM

Publishing Components With Visual Component Manager (VCM)


To make a component available for reuse through VCM, you must first publish that component in a VCM repository database. To publish a component through VCM from within the current project, you should take some preliminary steps to prepare the component for publication and to invoke VCM, as discussed in Step by Step 12.1. STEP BY STEP 12.1 Preparing to Publish a Component with Visual Component Manager If you intend to publish the component's source code, make sure that you have saved the project before publishing it. Otherwise, VCM will give you the warning Project must be saved before it can be published when you attempt to publish the component. 1. Click the VCM icon on the VB toolbar to invoke the VCM window (see Figure 12.10).

FIGURE 12.10 The main window for VCM 2. Choose a folder within the VCM repository by doubleclicking one on the tree in the left-hand window. 3. Invoke the VCM Publish wizard through one of three methods:

129

Drag a compiled component (*.DLL, *.OCX, or *.EXE) from Windows Explorer to a folder in the Visual Component Manager window. Make sure that you have opened a folder in the repository (double-click it in the VCM window). Then, rightclick in the folder's contents (the upper center and right pane of the VCM window). Click New on the resulting Visual Component Manager shortcut menu. Select the component's project window in Project Explorer and right-click the project name to invoke the project's shortcut menu (see Figure 12.11). Choose Publish on the project's shortcut menu. When you click Source Files, you will publish the project's source-code files; when you click Build Outputs, you will compile and publish its outputs. This third technique does not require you to have the VCM window open.

FIGURE 12.11 Visual Component Manager's Publish menu. In this figure, the developer has accessed the menu by right-clicking the Project's name in the Project Explorer. 4. Publish Wizard will display an introductory screen (not shown here). Click the Next button to proceed. 5. On the next screen (Repository/Folder), you can choose a VCM repository database (see Figure 12.12) and then a folder within that database (see Figure 12.13).

FIGURE 12.12 Initial view of VCM's Repository/Folder dialog box

130

FIGURE 12.13 VCM's Repository/Folder dialog box, showing available folders 6. Complete the Repository/Folder dialog box by editing, if necessary, the name that you want to give to the component. (This name is for informational purposes only and will not affect project or Registry names.) The completed dialog box should look like Figure 12.14. Click the Next button to continue to the Title and Properties dialog box.

FIGURE 12.14 VCM's Repository/Folder dialog box, showing a selected folder and the name of a component to add to VCM's repository database. 7. You typically don't change anything on the Title and Properties dialog box, although of course you can (see Figure 12.15). Click Next to proceed to the More Properties screen.

131

FIGURE 12.15. VCM's Title and Properties dialog box. 8. From the More Properties screen (Figure 12.16), you can add a component description and keywords. Other developers can use the description and keywords to find your component later.

FIGURE 12.16 A second Properties screen in VCM enables you to add a component description and keywords. 9. You can add a new keyword to the component by clicking the large plus button (+) on the More Properties screen and filling in the keyword text on the resulting dialog box (see Figure 12.17).

FIGURE 12.17. Adding a new keyword to a component in VCM.

132

10. Click the Next button to proceed to the next Publish Wizard screen. A list of files that will be published as part of the component appears, as shown in Figure 12.18. Depending on whether you chose to publish source code or the compiled component (see step 3), you will either see the source-code files for the project or the compiled file (and any known files that the compile file depends on to run). At this point, you can add or take away files. After you have finished, click the Next button to proceed to the next screen.

FIGURE 12.18 You can add or delete files from a published component's source code with the VCM Select Additional File(s) dialog box. 11. On the next screen, you can indicate whether any of the files that you are publishing with the COM component require an entry in the Windows Registry (see Figure 12.19). Usually you need to do nothing on this screen, because VCM is pretty good at guessing which files need registration. In fact, if you try to check a file that obviously doesn't need registration (such as a source-code file), VCM will warn you that the file doesn't need registration and will refuse to check the box next to the file's name.

FIGURE 12.19 You can indicate the files that belong to a published component and that must be registered in the Windows Registry. 12. After you have made any adjustments to your component's registration information, you can click the Next button. This brings up the Publish wizard's final screen (the Finish screen). Click the Finish button on this screen to finalize your component's entry in the VCM repository database. Storing VCM Information in Repository Databases Making VCM Available in the VB IDE Publishing Components With VCM Finding and Reusing Components With VCM

133

You might also like