Visual Basic Concepts
Understanding Control Lifetime and Key Events
Designing ActiveX controls involves a radical shift in perspective. The key events you must respond to are different — for example, your life will revolve around the Resize event — and there's no such thing as QueryUnload. But that's just the beginning.
"Control Creation Terminology," earlier in this chapter, introduced the idea that a control is not a permanent fixture of a form. Indeed, design-time and run-time instances of your control will be created and destroyed constantly — when forms are opened and closed, and when you run the project.
Each time an instance of your ActiveX control is created or destroyed, the UserControl object it's based on is created or destroyed, along with all of its constituent controls. ("The UserControl Object," earlier in this chapter, explains the basis of all ActiveX controls created with Visual Basic.)
Consider, for example, a day in the life of the ShapeLabel control used in the step-by-step procedures in "Creating an ActiveX Control."
The user creates an instance of ShapeLabel — by double-clicking on the Toolbox, or by opening a form on which an instance of ShapeLabel was previously placed.
The constituent controls, a Shape and a Label, are created.
The UserControl object is created, and the Shape and Label controls are sited on it.
The UserControl_Initialize event procedure executes.
The ShapeLabel control is sited on the form.
If the user is placing a new ShapeLabel, the InitProperties event of the UserControl object occurs, and the control's default property values are set. If an existing form is being opened, the ReadProperties event occurs instead, and the control retrieves its saved property values.
The UserControl_Resize event procedure executes, and the constituent controls are resized according to the size the user made the new control instance, or the size they were before the form was closed.
The Show and Paint events occur. If there are no constituent controls, the UserControl object draws itself.
The user presses F5 to run the project. Visual Basic closes the form.
The UserControl object's WriteProperties event occurs, and the control's property values are saved to the in-memory copy of the .frm file.
The control is unsited.
The UserControl object's Terminate event occurs.
The UserControl object and its constituent controls are destroyed.
And that's not the half of it. The run-time instance of the form is now created, along with a run-time instance of the ShapeLabel control. When the user closes the form and returns to design mode, the ShapeLabel is destroyed and re-created once again.
The rest of this topic explains the key events in a UserControl object's life, and provides reference lists of the events you receive in several important scenarios.
Key UserControl Events
The meanings of the key events in the life of a UserControl object are as follows:
The Initialize event occurs every time an instance of your control is created or re-created. It is always the first event in a control instance's lifetime.
The InitProperties event occurs only in a control instance's first incarnation, when an instance of the control is placed on a form. In this event, you set the initial values of the control's properties.
The ReadProperties event occurs the second time a control instance is created, and on all subsequent re-creations. In this event, you retrieve the control instance's property values from the in-memory copy of the .frm file belonging to the form the control was placed on.
The Resize event occurs every time a control instance is re-created, and every time it is resized — whether in design mode, by the developer of a form, or at run time, in code. If your UserControl object contains constituent controls, you arrange them in the event procedure for this event, thus providing your control's appearance.
The Paint event occurs whenever the container tells the control to draw itself. This can occur at any time, even before the control receives its Show event — for example, if a hidden form prints itself. For user-drawn controls, the Paint event is where you draw your control's appearance.
The WriteProperties event occurs when a design-time instance of your control is being destroyed, if at least one property value has changed. In this event, you save all the property values a developer has set for the control instance. The values are written to the in-memory copy of the .frm file.
The Terminate event occurs when the control is about to be destroyed.
In addition to the events listed above, the Show and Hide events may be important to your control. Show and Hide occur as indicated in Figure 9.5.
Figure 9.5 Show and Hide Events
In order to draw to the screen in Windows, any control must have a window, temporarily or permanently. Visual Basic ActiveX controls have permanent windows. Before a control has been sited on a form, its window is not on the container. The UserControl object receives Show and Hide events when the window is added and removed.
While the control's window is on the form, the UserControl receives a Hide event when the control's Visible property changes to False, and a Show event when it changes to True.
The UserControl object does not receive Hide and Show events if the form is hidden and then shown again, or if the form is minimized and then restored. The control's window remains on the form during these operations, and its Visible property doesn't change.
If the control is being shown in an internet browser, a Hide event occurs when the page is moved to the history list, and a Show event occurs if the user returns to the page.
Note If your control is used with earlier versions of Visual Basic, the UserControl object will not receive Show and Hide events at design time. This is because earlier versions of Visual Basic did not put any visible windows on a form at design time.
For More Information The topic "Life and Times of a UserControl Object," one of the step-by-step procedures in "Creating an ActiveX Control," demonstrates the key events in the life of a control and illustrates how often control instances are created and destroyed.
The Incarnation and Reincarnation of a Control Instance
Let's follow a control instance from its placement on a form, through subsequent development sessions, until it's compiled into an application. We'll assume the control was already developed and compiled into an .ocx file, before the curtain opens.
The scenarios that follow mention both Resize and Paint events. Which event you're interested in depends on the control creation model you're using, as discussed in "Three Ways to Build ActiveX Controls," earlier in this chapter.
If your control provides its appearance using constituent controls, you'll use the Resize event to size the constituent controls. If you're authoring a user-drawn control, on the other hand, you can ignore the Resize event and remarks about constituent controls. User-drawn controls draw their appearance in the Paint event. This is discussed in "Drawing Your Control," later in this chapter.
Note In all of these scenarios, the order and number of Resize and Paint events may vary.
The Control Instance is Placed on a Form
When you double-click a control's icon in the Toolbox, a design-time instance of the control is placed on the form you're designing. The following events occur in the UserControl object at the heart of the control instance:
Event | What gets done |
Initialize | Constituent controls have been created, but the control has not been sited on the form. |
InitProperties | The control instance sets default values for its properties. The control has been sited, so the Extender and AmbientProperties objects are available. This is the only time the instance will ever get this event. |
Resize, Paint | The control instance adjusts the size of its constituent controls, if any, according to its default property settings. A user-drawn control draws itself. |
The developer of the form can now see the control, and set its properties in the Properties window. After the developer does this, she may press F5 to run the project.
From Design Mode to Run Mode
When F5 is pressed, the control's design-time instance on the form is destroyed. When the form is loaded at run time, the control is recreated as a run-time instance.
Event | What gets done |
WriteProperties | Before the design-time instance is destroyed, it has a chance to save property values to the in-memory copy of the .frm file. |
Terminate | Constituent controls still exist, but the design-time control instance is no longer sited on the form. It's about to be destroyed. |
Initialize | Constituent controls have been created, but the run-time control instance has not been sited on the form. |
ReadProperties | The control instance reads the property values that were saved in the in-memory .frm file. The control has been sited on the run-time instance of the form, so the Extender and AmbientProperties objects are available. |
Resize, Paint | The control instance adjusts the size of its constituent controls, if any, according to its current property settings. A user-drawn control draws itself. |
The developer tests the form by clicking the control, or taking other actions that cause the control's properties, methods, and events to be exercised.
From Run Mode to Design Mode
Finally the developer closes the form and returns to design mode. The run-time instance of the control is destroyed, and a design-time instance is created:
Event | What gets done |
Terminate | The run-time instance never gets a chance to save property settings. Changes to property values while the program was running are discarded. |
Initialize | Design-time instances of constituent controls have been created, but the design-time control instance has not been sited on the form. |
ReadProperties | The control reads the property values that were saved in the in-memory copy of the .frm file. The control has been sited on the design-time instance of the form, so the Extender and AmbientProperties objects are available. |
Resize, Paint | The control instance adjusts the size of its constituent controls, if any, according to its saved property settings. A user-drawn control draws itself. |
Closing the Form
If the developer doesn't need to work on the form any more, she may close it. Or it may be quitting time, and she may close the whole project. In either case, the control instance on the form is destroyed.
Event | What gets done |
WriteProperties | Before the design-time instance is destroyed, it has a chance to save property values to the in-memory copy of the .frm file. |
Terminate | Constituent controls still exist, but the control instance is no longer sited on the form. It's about to be destroyed. |
Note In all of the scenarios above, the control instance has been saving its property values to the in-memory copy of the .frm file. If the developer chooses not to save the project before closing it, those property settings will be discarded.
Additional Scenarios
When the developer re-opens the project, and opens the form to work on it again, the control is reincarnated as a design-time instance. It receives Initialize, ReadProperties, Resize, Paint, and WriteProperties events.
Note A WriteProperties event? Yes, indeed. When the project is opened, Visual Basic creates an in-memory copy of the .frm file. As each control on the form is created, it gets a ReadProperties event to obtain its saved property values from the .frm file, and a WriteProperties event to write those property values to the in-memory copy of the .frm file.
Compiling the Project
When the project is compiled into an application or component, Visual Basic loads all the form files invisibly, one after another, in order to write the information they contain into the compiled file. A control instance gets the Initialize, ReadProperties, and WriteProperties events. The control's property settings are compiled into the finished executable.
Running the Compiled Program or Component
Whenever a user runs the installed application or component, and the form is loaded, the control receives Initialize, ReadProperties, and Resize events. When the form is unloaded, the control receives a Terminate event.
Controls on World Wide Web Pages
A control on an HTML page is specified using the HTML <OBJECT> and </OBJECT> tags. When the HTML is processed, the control is created and positioned. If the <OBJECT> tag includes any <PARAM NAME> attributes, the property values supplied with those attributes are passed to the control's ReadProperties event using the standard PropertyBag object, as discussed earlier in this topic.
Once the HTML page is active, the control's property values may also be set by scripts attached to events that occur on the page.
Note If there are no <PARAM NAME> attributes other than those that set extender properties, the control may receive an InitProperties event rather than a ReadProperties event. This behavior is dependent on browser implementation, and should not be relied on.
Events You Won't Get in a UserControl object
Some events you're familiar with from working with forms don't exist in a UserControl object. For example, there is no Activate or Deactivate event, because controls are not activated and deactivated the way forms are.
More striking is the absence of the familiar Load, Unload, and QueryUnload events. Load and Unload simply don't fit the UserControl lifestyle; unlike a form, a control instance isn't loaded at some point after it's created — when a UserControl object's Initialize event occurs, constituent controls have already been created.
The UserControl object's Initialize and ReadProperties events provide the functionality of a form's Load event. The main difference between the two is that when the Initialize event occurs, the control has not been sited on its container, so the container's Extender and AmbientProperties objects are not available. The control has been sited when ReadProperties occurs.
Note ReadProperties doesn't occur the first time a control instance is placed on a container — in that case the InitProperties occurs instead.
The UserControl event most like a form's Unload event is Terminate. The constituent controls still exist at this point, although you no longer have access to the container, because your control has been unsited.
The WriteProperties event cannot be used as an analog of Unload, because it occurs only at design time.
UserControl objects don't have QueryUnload events because controls are just parts of a form; it's not up to a control to decide whether or not the form that contains it should close. A control's duty is to destroy itself when it's told to.
Events Peculiar to UserControls
The GotFocus and LostFocus events of the UserControl object notify user-drawn controls when they should show or stop showing a focus rectangle. These events should not be forwarded to the user of your control, because the container is responsible for focus events.
If your UserControl has constituent controls that can receive focus, the EnterFocus event will occur when the first constituent control receives the focus, and the ExitFocus event will occur when focus leaves the last constituent control. See "How to Handle Focus in your Control," later in this chapter.
If you have allowed developers to set access keys for your control, the AccessKeyPress event occurs whenever a user presses an access key. See "Allowing Developers to Set Access Keys for Your Control," later in this chapter. The AccessKeyPress event can also occur if your control is a default button or cancel button. This is discussed in "Allowing Your Control to be a Default or Cancel Button," later in this chapter.
The AmbientChanged event occurs whenever an Ambient property changes on the container your control has been placed on. See "Using the AmbientProperties Object to Stay Consistent with the Container," later in this chapter.