Visual Basic Concepts
Life Cycle of Visual Basic Forms
Because they're visible to the user, forms and controls have a different life cycle than other objects. For example, a form will not close just because you've released all your references to it. Visual Basic maintains a global collection of all forms in your project, and only removes a form from that collection when you unload the form.
In similar fashion, Visual Basic maintains a collection of controls on each form. You can load and unload controls from control arrays, but simply releasing all references to a control is not sufficient to destroy it.
*For More Information* The Forms and Controls collections are discussed in "Collections in Visual Basic" earlier in this chapter.
States a Visual Basic Form Passes Through
A Visual Basic form normally passes through four states in its lifetime:
Created, but not loaded.
Loaded, but not shown.
Memory and resources completely reclaimed.
There's a fifth state a form can get into under certain circumstances: Unloaded and unreferenced while a control is still referenced.
This topic describes these states, and the transitions between them.
Created, But Not Loaded
The beginning of this state is marked by the Initialize event. Code you place in the Form_Initialize event procedure is therefore the first code that gets executed when a form is created.
In this state, the form exists as an object, but it has no window. None of its controls exist yet. A form always passes through this state, although its stay there may be brief.
For example, if you execute
Form1.Show, the form will be created, and Form_Initialize will execute; as soon as Form_Initialize is complete, the form will be loaded, which is the next state.
The same thing happens if you specify a form as your Startup Object, on the General tab of the Project Properties dialog box (which is available from the Project menu). A form specified as the Startup Object is created as soon as the project starts, and is then immediately loaded and shown.
*Note* You can cause your form to load from within Form_Initialize, by calling its Show method or by invoking its built-in properties and methods, as described below.
Remaining Created, But Not Loaded
By contrast, the following code creates an instance of Form1 without advancing the form to the loaded state:
Dim frm As Form1 Set frm = New Form1
Once Form_Initialize has ended, the only procedures you can execute without forcing the form to load are Sub, Function, and Property procedures you've added to the form's code window. For example, you might add the following method to Form1:
Public Sub ANewMethod() Debug.Print "Executing ANewMethod" End Sub
You could call this method using the variable
frm (that is,
frm.ANewMethod) without forcing the form on to the next state. In similar fashion, you could call ANewMethod in order to create the form:
Dim frm As New Form1 frm.ANewMethod
frm is declared As New, the form is not created until the first time the variable is used in code — in this case, when ANewMethod is invoked. After the code above is executed, the form remains created, but not loaded.
Form1.ANewMethod, without declaring a form variable, has the same effect as the example above. As explained in "Customizing Form Classes," Visual Basic creates a hidden global variable for each form class. This variable has the same name as the class; it's as though Visual Basic had declared
Public Form1 As New Form1.
You can execute as many custom properties and methods as you like without forcing the form to load. However, the moment you access one of the form's built-in properties, or any control on the form, the form enters the next state.
*Note* You may find it helpful to think of a form as having two parts, a code part and a visual part. Before the form is loaded, only the code part is in memory. You can call as many procedures as you like in the code part without loading the visual part of the form.
The Only State All Forms Pass Through
Created, But Not Loaded is the only state all forms pass through. If the variable
frm in the examples above is set to Nothing, as shown here, the form will be destroyed before entering the next state:
Dim frm As New Form1 frm.ANewMethod Set frm = Nothing ' Form is destroyed.
A form used in this fashion is no better than a class module, so the vast majority of forms pass on to the next state.
Loaded, But Not Shown
The event that marks the beginning of this state is the familiar Load event. Code you place in the Form_Load event procedure is executed as soon as the form enters the loaded state.
When the Form_Load event procedure begins, the controls on the form have all been created and loaded, and the form has a window — complete with window handle (hWnd) and device context (hDC) — although that window has not yet been shown.
Any form that becomes visible must first be loaded.
Many forms pass automatically from the Created, But Not Loaded state into the Loaded, but Not Shown state. A form will be loaded automatically if:
The form has been specified as the Startup Object, on the General tab of the Project Properties dialog box.
The Show method is the first property or method of the form to be invoked, as for example
The first property or method of the form to be invoked is one of the form's built-in members, as for example the Move method.
*Note* This case includes any controls on the form, because each control defines a property of the form; that is, in order to access the Caption property of Command1, you must go through the form's Command1 property:
The Load statement is used to load the form, without first using New or As New to create the form, as described earlier.
Forms That Are Never Shown
In the first two cases listed above, the form will continue directly on to the visible state, as soon as Form_Load completes. In the last two cases, the form will remain loaded, but not shown.
It has long been common coding practice in Visual Basic to load a form but never show it. This might be done for several reasons:
To use the Timer control to generate timed events.
To use controls for their functionality, rather than their user interface — for example, for serial communications or access to the file system.
To execute DDE transactions.
*Note* With the Professional or Enterprise edition, you can create ActiveX components (formerly called OLE servers), which are often better at providing code-only functionality than controls are. See Creating ActiveX Components in the Component Tools Guide.
Always Coming Home
Forms return from the visible state to the loaded state whenever they're hidden. Returning to the loaded state does not re-execute the Load event, however. Form_Load is executed only once in a form's life.
Once a form becomes visible, the user can interact with it. Thereafter, the form may be hidden and shown as many times as you like before finally being unloaded.
Interlude: Preparing to Unload
A form may be either hidden or visible when it's unloaded. If not explicitly hidden, it remains visible until unloaded.
The last event the form gets before unloading is the Unload event. Before this event occurs, however, you get a very important event called QueryUnload. QueryUnload is your chance to stop the form from unloading. If there's data the user might like to save, this is the time to prompt the user to save or discard changes.
*Important* Setting the Cancel argument of the QueryUnload to True will stop the form from unloading, negating an Unload statement.
One of most powerful features of this event is that it tells you how the impending unload was caused: By the user clicking the Close button; by your program executing the Unload statement; by the application closing; or by Windows closing. Thus QueryUnload allows you to offer the user a chance to cancel closing the form, while still letting you close the form from code when you need to.
*Important* Under certain circumstances, a form will not receive a QueryUnload event: If you use the End statement to terminate your program, or if you click the End button (or select End from the Run menu) in the development environment.
*For More Information* See "QueryUnload Event" in the Language Reference.
Returning to the Created, But Not Loaded State
When the form is unloaded, Visual Basic removes it from the Forms collection. Unless you've kept a variable around with a reference to the form in it, the form will be destroyed, and its memory and resources will be reclaimed by Visual Basic.
If you kept a reference to the form in a variable somewhere, such as the hidden global variable described in "Customizing Form Classes," then the form returns to the Created, But Not Loaded state. The form no longer has a window, and its controls no longer exist.
The object is still holding on to resources and memory. All of the data in the module-level variables in the form's code part are still there. (Static variables in event procedures, however, are gone.)
You can use that reference you've been keeping to call the methods and properties that you added to the form, but if you invoke the form's built-in members, or access its controls, the form will load again, and Form_Load will execute.
Memory and Resources Completely Reclaimed
The only way to release all memory and resources is to unload the form and then set all references to Nothing. The reference most commonly overlooked when doing this is the hidden global variable mentioned earlier. If at any time you have referred to the form by its class name (as shown in the Properties Window by the Name property), you've used the hidden global variable. To free the form's memory, you must set this variable to Nothing. For example:
Set Form1 = Nothing
Your form will receive its Terminate event just before it is destroyed.
*Tip* Many professional programmers avoid the use of the hidden global variable, preferring to declare their own form variables (for example,
Dim dlgAbout As New frmAboutBox) to manage form lifetime.
*Note* Executing the End statement unloads all forms and sets all object variables in your program to Nothing. However, this is a very abrupt way to terminate your program. None of your forms will get their QueryUnload, Unload, or Terminate events, and objects you've created will not get their Terminate events.
Unloaded and Unreferenced, But a Control Is Still Referenced
To get into this odd state, you have to unload and free the form while keeping a reference to one of its controls. If this sounds like a silly thing to do, rest assured that it is.
Dim frm As New Form1 Dim obj As Object frm.Show vbModal ' When the modal form is dismissed, save a ' reference to one of its controls. Set obj = frm.Command1 Unload frm Set frm = Nothing
The form has been unloaded, and all references to it released. However, you still have a reference to one of its controls, and this will keep the code part of the form from releasing the memory it's using. If you invoke any of the properties or methods of this control, the form will be reloaded:
obj.Caption = "Back to life"
The values in module-level variables will still be preserved, but the property values of all the controls will be set back to their defaults, as if the form were being loaded for the first time. Form_Load will execute.
*Note* In some previous versions of Visual Basic, the form did not completely re-initialize, and Form_Load did not execute again.
*Note* Not all forms behave as Visual Basic forms do. For example, the Microsoft Forms provided in Microsoft Office don't have Load and Unload events; when these forms receive their Initialize events, all their controls exist and are ready to use.
*For More Information* Forms are discussed in "Designing a Form" in "Forms, Controls, and Menus" and in "More About Forms" in "Creating a User Interface."