During this hour you will learn
Are you ready for a whirlwind tour? This lesson shows how to create ActiveX controls. Just as this tutorial can't teach you Visual Basic in a single lesson, neither can this tutorial teach you how to create ActiveX controls in a single lesson. Even a simple ActiveX control is extremely difficult to program, and complicated ActiveX controls add multiple layers of difficulty on top of that. Nevertheless, the more a control does, the less you subsequently have to do in every application in which you use that control.
As you learned from Hour 30, an ActiveX control isn't just for Visual Basic. No matter which programming platform you work with in the future (assuming ActiveX compatibility as most of them possess these days), you'll be able to use the ActiveX controls that you built with VB without modifying the controls or working the code to insert the control. All you'll need to do is drop in the control by using the platform's normal ActiveX control insertion method (such as VB's Components dialog box).
In object-oriented programming languages, such as C++, a class is a collection of data properties and procedures that define specific objects. A simple data type, such as Integer, isn't a class because the data type has properties (whole-number representations) but not procedures. When you design and create ActiveX controls, you must first design a class that describes the control.
This topic examines objects in more theoretical detail than Hour 20, "Understanding Objects and Using the Object Browser," because you must work more closely with classes (which describe an object's properties, methods, and event procedures) when designing ActiveX controls. You'll now learn how to design classes so that you can create brand-new ActiveX objects-specifically, new controls for the Toolbox window. In most programming languages that support classes, studying class design would comprise half the book. In Visual Basic, studying classes requires a lot less instruction, especially when you have an example to build a simple ActiveX control, as this lesson does.
Only those of you with VB's Professional or Enterprise editions can create ActiveX controls because the Control Creation Edition (CCE) is included in those editions. If, however, you use the Standard Edition, you're not out in the cold because you can download VB's Creation Control Edition. At the time of this writing, and seemingly for the future, Microsoft is giving away VB's Creation Control Edition that Standard Edition users can add to their system.
To obtain the software, point your Web browser to http://www.microsoft.com/vbasic/. The download takes about 30 minutes, so grab a bite to eat while you wait.
If you download the Creation Control Edition, you'll have two separate copies of VB on your system: the Standard Edition and the Creation Control Edition. To follow this hour's lesson, you'll work not from VB 5 as you've done throughout this book but from the Creation Control Edition. Fortunately, the two editions' interface and development environments are identical because the Creation Control Edition supports the same toolbars, menu bar, Toolbox, Properties window, Project window, and Form window as the regular VB environment.
![]()
A control is nothing more than an example, or instance, of a Visual Basic class. Therefore, when you insert a new form in an application, the new form is an instance of the Form class. The Form class is the definition of a form, and the class distinguishes forms from other objects such as a command button.
The Toolbox holds the class or the definition of controls, and when you place a control onto a form, you instantiate the control. You instantiate specific objects. The Toolbox window holds the control's class, and when you double-click a Toolbox tool to add a control to your Form window, the class generates an object on your form. ActiveX controls support the class definition because their properties and procedures work together to form controls.
As Figure 31.1 shows, a control has both data (property values) and code (methods and event procedures). Therefore, a control is part of a class that defines those members that are data and code.
A control is a package of data and procedure members.
There's a great aspect about VB programmers who are newcomers to classes and objects: VB programmers, unlike programmers who work with true object-oriented languages such as C++, have little or no trouble accepting the definition of classes and objects. Using controls has been an integral part of their VB development since they placed their first command button in a Form window. In other words, you already understand that a Text Box control is a package containing data members that are property values that make the control unique. You also know that code members are events and methods that respond to the control or make the control do something.
VB can create ActiveX controls by using three methods:
The ActiveX control that you create, with any of the three methods described in the preceding list, will look and act like other controls. You can insert them into the Toolbox window, double-click the control to add the control to the Form window, and select control properties from the Properties window. Also, the control can support special enumerated properties, such as the Alignment property that drops down a list of choices, and the Property Pages dialog, such as the one that appears when you click the Font property's ellipsis. If the control isn't to appear on the running Form window (such as the case with the Timer control), you can set the control's InvisibleAtRunTime property to True, and the control will work in the background when needed rather than appear on the end user's form.
Subclassing somewhat simulates but doesn't replace inheritance in pure OOP languages such as C++ and SmallTalk. VB can't inherit from a parent class, but VB can use the features of a parent class with property-mapping mechanisms. As a result, VB seems to inherit subclassed objects, but the inheritance mechanism is fairly primitive. VB won't be a true object-oriented language, despite the strong OOP ties that VB supports, until Microsoft adds inheritance features to the language.
![]()
With this example, you begin creating a new control that you'll continue to build throughout the rest of the lesson. As you follow this and subsequent examples, you'll no doubt consider the following:
This example is going to subclass the Text Box control to create a new control named the UL Text Box control. This control is nothing more than a Text Box control with one addition: The UL Text Box control will contain a property called ULText that determines whether the text in the UL Text Box control should appear in all uppercase letters, all lowercase letters, or as the programmer entered the text. The ULText will produce a drop-down list box property that includes these three possible values. You'll have to write some code to respond to the property's change-that is, when the programmer changes the ULText property, code will have to convert the text to uppercase or lowercase letters.
To preserve some page count space (and a little sanity), after the programmer changes the text to uppercase or lowercase, the original state of the text will be lost. Therefore, any application that uses the UL Text Box control will be able to display the text the way the user or the way the programmer inserted the text in the Text property. After the programmer uses the ULText property to convert the text to all uppercase or lowercase letters, however, the original state will be lost. From then on, the programmer can only convert the text to uppercase or lowercase letters.
![]()
To begin building the new control, open a new project and select ActiveX Control from the New Project dialog box. If you use VB's Control Creation application, you also will open a new project and select the ActiveX Control icon. Although Professional and Enterprise edition programmers can access the Control Creation icon from within Visual Basic, Standard Edition programmers have to start the separate application. All subsequent steps are identical for each edition.
The Visual Basic development environment looks no different from all the other sessions you've seen when creating new projects. Notice the name on VB's title bar: UserControl1. Notice also the type of object described in parentheses on the title bar: (UserControl). You're creating something called a user control, and VB assigns the default name of UserControl1 to the user control. The term user is somewhat misleading because programmers rather than end users will use the control that in their applications.
Visual Basic generates a default control class called UserControl when you begin to create a new control. VB assigns default properties, events, and methods to the control, and the rest of your work session requires modifying these default properties and code-related parts of the instantiated UserControl1 to make the control behave the way you want it to. All ActiveX controls subclass from this overall class, UserControl, hence the name.
When creating controls, the concept of runtime and design time can confuse programmers. Whereas a control's design time occurs when you create and edit the control, a control's runtime occurs in two ways. When a programmer inserts the control into an application at the application's design time, the control is a compiled and executing control that responds to the programmer's setup instructions. Also, when the programmer compiles and runs the application, the control is also running, except that the control runs and responds to an end user. Often, programmers distinguish the two kinds of control runtimes by calling then design time running and run time running. Generally, the context of the run mode is obvious as you create and work with the control.
![]()
It's time to customize the control. Therefore, display the Project
Properties dialog box and type ULTextBoxControl in the
Project Name text and A text box with case-conversion into
the Project Description text box. This description appears
in subsequent Project Properties dialog boxes for applications
that you insert the control into. Close the dialog box and change
the control's Name property to read ULText.
You should now change the default graphic image used for the control. The image will appear on the Toolbox when you insert the tool into subsequent application's Toolbox windows.
To change the image, select the ToolboxBitmap property and click the ellipsis that appears. Search VB's \Graphics\Bitmaps\Assorted folder and select the Notebook file. Be warned that a really good graphic doesn't exist that matches this lesson's new control. Instead of a notepad, a modified Text Box control graphic would be more appropriate. You might have to design your own small bitmap images with Windows Paint if you can't find good images for the controls that you create. This lesson uses the Notepad graphic to keep things as simple as possible. The image won't appear in the control's Project Windows but will appear in subsequent applications that use the control.
If you create your own bitmap image, size the image down to 15 by 16 screen pixels so that the image appears in the same relative size as the other Toolbox control images.
![]()
Your Project window changes to reflect the new named status of your project and control (see Figure 31.2).
The Project window now describes your project and control names.
Save your project. Visual Basic asks for the control file name and project name. A control's source application uses the .CTL file name extension, but you'll ultimately compile the control into a usable OCX control before this lesson is over.
Now that you've set the stage for the control's creation by creating a new project and control and naming all the components, you're ready to work with the parent Text Box control so that you can subclass the original Text Box control. Fortunately, Visual Basic supplies a Control Interface Wizard that you can run to ease the subclassing requirements.
Most of this topic section consists of the "Example" and "Next Step" sections. Although you could learn all the theory behind the subclassing first, the UL Text Box control lends itself to a fairly streamlined development (compared to more complicated ActiveX controls). This topic section's example will use the wizard to add properties, methods, and events to your new control and to request the properties, methods, and events that you want to keep from the parent Text Box control.
Double-click the Text Box control to add the intrinsic Text Box control to your new control's control object window. Name the text box txtULParent. You've just embedded the Text Box control into your new UL Text Box control, and the UL Text Box control will use the embedded Text Box control to do its job. The text box will be hidden from view, and the UL Text Box control will use the text box. Programmers who use the UL Text Box control, however, will not work with the text box. The embedded text box acts like a local object variable within the UL Text Box control. The text box is hidden from the outside world, but the UL Text Box control has full access to the text box and will borrow abundantly from its properties, events, and methods.
From the Add-Ins menu choose Add-In Manager to include
the add-in ActiveX Control Interface Wizard in your development
environment. Click OK to close the Add-In Manager dialog box and
to add the wizard to the Add-Ins menu. Pull down the Add-Ins
menu again and choose ActiveX Control Interface Wizard to start
the wizard's Introduction dialog box. When you click Next
to move past the Introduction, you'll see Figure 31.3's Select
Interface Members dialog box.
The wizard suggests properties, events, and methods that you might want to use.
The Available Names list box in Figure 31.3 displays all available properties, events, and methods that you can select for your project. The Selected Names list box lists the new control's selected properties, events, and methods. In other words, the Available Names list box contains those properties, events, and methods that the wizard predicted that you might want to include in your control. The wizard guesses fairly well, but you can change the wizard's guess if needed.
Remove the MouseMove property from the Selected Names list by selecting the property and clicking the < command button. When building your own controls, you should scan the list and remove whatever properties, events, and methods the wizard selected but that you don't want in the new control. Although you can leave it, the MouseMove event isn't an extremely useful event for text box controls, and removing the event gives you some practice with the dialog box. Also, remove the BackStyle property that has no bearing on the UL Text Box control.
Often, more difficult than removing items is adding property, event, and method items to the list. You must know which ones your new control needs to support as well as the ones already selected. Select the following items in the Available Names list box and click the > button to move them to the Selected Names list box: Alignment, Change, FontBold, FontItalic, FontName, FontSize, FontStrikethru, FontUnderline, MultiLine, PasswordChar, ScrollBars, SelLength, SelStart, SelText, Text, and ToolTipText.
To select multiple items in the Available Names list box, Ctrl+clicking the items and then click the > button to send all the selected items to the Selected Names list box.
![]()
Click the Next button to move to the next dialog box. You
must add any new members to the set of events, properties, and
methods that the UL Text Box control will support. Click the New
button to display the Add Custom Member dialog box (see Figure
31.4).
Add your new properties, events, and methods in the Add Custom Member dialog box.
Type ULText in the Name text box, leave the Property
option button selected, and click OK. ULText is the new property
you're adding that will distinguish the UL Text Box control from
a normal text box, as explained in the previous topic section.
Click Next to move to the next dialog box, where you'll
map the properties, events, and methods to the text box equivalents.
To map all but the new ULText property to the text box named txtULParent,
select all properties, events, and methods except ULText
in the Public Name list box, and select txtULParent from the Control
drop-down list box. Your dialog box should look something like
the one in Figure 31.5.
All properties, events, and methods except for the new one maps to the regular text box.
If you select an individual name in the Public Name list box (except ULText), the Control box shows the control that the name maps to. All of these controls map to the text box, of course. The Member box shows the corresponding property, event, or method that the name maps to within the Text Box control. All the names map to corresponding member names. However, you could change the mapping and make a MouseDown event in your new control map to a MouseClick event if you wanted to change the way that the new control responds from its normal state.
Click Next to show Figure 31.6's Set Attributes dialog
box. The dialog box requests information about any unmapped properties,
events, and methods that appear in your new control. Obviously,
the ULText property has no mapping to an existing property, and
ULText happens to be the only unmapped property in the control.
Set the mapping for all unmapped properties, events, and methods in the Set Attributes dialog box.
Normally, you might need to change the Data Type, Default Value, Run Time, and Design Time values listed in the Attribute Information section. The default values happen to be correct already, so you need to enter only a description for the new property, which will appear at the bottom of the Properties window when a programmer selects the control. Type the following description in the Description text box:
Sets the text to uppercase, lowercase, or no change
Click Next to display the Finished dialog box, and then
click Finish to create the control's shell and display
some information about the created control. The information describes
the rest of the control's creation process that you must perform.
As you'll see in the next section, you must manually work on the
rest of the control because the wizard's job is done.
Save the new control's project now so that it's safely tucked away.
Notice that the preceding section called the new control a shell. Didn't the wizard create the control? The ActiveX Control Interface Wizard is just a guide that helps you map properties, events, and methods to existing properties, events, and methods, and describes any new properties, events, and methods that you might have added to the new control. The wizard did nothing to activate the ULText property because the wizard has no idea what you want to use ULText for. To activate ULText, you must modify the code that the wizard generated for the control.
From the View menu choose Code to look at the Code
window. Much of the Code window is devoted to mapping the new
control's properties, events, and methods to the underlying txtULParent
text box control's properties, events, and methods. In other words,
when the programmer who uses this new control sets, at design
time running, the BackColor property, the code actually sets the
underlying text box's BackColor property.
Although this lesson keeps things as simple as possible, the wizard's generated code can require either a small edit or a major overhaul. No matter how much overhaul is needed, you do need to work on the parts of the Code window that contain remarks beginning with TO DO because the wizard uses those remarks for placeholders if you later modify the code.
Guard against modifying remarks prefixed with the following so the wizard will be able to modify the code later:
'WARNING! DO NOT REMOVE OR MODIFY THE FOLLOWING COMMENTED LINES!
Unless you base your new ActiveX control on a list-based parent control, such as the List Box control, you won't see TO DO remarks. The wizard can't manipulate list-based parent controls, and you'll have to handle the routines that process lists yourself.
![]()
You can change the default ULText value from 0 to a different value by changing the second line in the Code window. (The first line is a remark.) Also, recall from this lesson's first topic section that a member is the property, event, or method that's part of the object. Only one member exists for the UL Text Box control (other than the ones subclassed from the parent), and the fourth line in the Code window names that member. The following provides the opening lines of the Code window that describes the default value and member declaration:
'Default Property Values: Const m_def_ULText = 0 'Property Variables: Dim m_ULText As Variant
The Const keyword creates named literals. Therefore, m_def_ULText isn't a variable but a constant that, when used in the rest of the program, is the same as 0 (or whatever you change the named literal to). Named literals are sometimes called named constants, hence the Const keyword.
![]()
The next several lines declares event procedures. The nature of Visual Basic controls requires that all event procedure code within an ActiveX control be declared just as the variables that you declare before you use them. These event procedure declarations are called prototypes because they prototype, or model, the procedures that follow. Visual Basic needs to know these prototypes in advance before the event procedure code actually appears inside ActiveX controls.
If the code contains no TO DO remarks, as this lesson's code doesn't, your only job is to modify the new control's sizing capabilities and to add the new property's functionality to the control. Before handling the executing code, you need to set up the three drop-down list box values that will appear when the programmer who uses this control clicks the ULText property. Set up three values from which the programmer can choose: AsIs, Uppercase, and Lowercase.
To program these values, you need a special declaration block called an enumeration block. Before learning the details, enter the following code directly below the declarations in the Code window's General section:
Public Enum ULTextEnum AsIs = 0 Uppercase = 1 Lowercase = 2 End Enum
The special Enum block tells the ActiveX control compiler which items should appear in a property drop-down list box. Code that you add later will use these enumerated values, which will appear as follows when the programmer clicks the ULText property in the UL Text Box control's Properties window:
0 - AsIs 1 - Uppercase 2 - Lowercase
The UL Text Box control's sizing code is extremely simple because the new control should size just as the parent text box would size. Often, a new ActiveX control should size differently from the control or controls that it subclasses from, especially for subclassed aggregate ActiveX controls. If a one-to-one correspondence occurs with the parent's and the new control's size, however, you can add a UserControl_Resize() event procedure as follows by selecting the Resize procedure from the Code window:
Private Sub UserControl_Resize() ' Set the height and scaling to the underlying control ' Stretch the control to the width and height txtULParent.Move 0, 0, ScaleWidth End Sub
Now that the resizing is out of the way, your primary job is to set up the enumerated value display and to convert the UL Text Box control to uppercase or lowercase, depending on the ULText property. The wizard created code for the ULText property, but you must fill in the details. The only two procedures you need to worry about here (because the control's only added functionality is a single property that the programmer can set or use) are the Property Get ULText() and the Property Let ULText() procedures. The Property Get and Property Let keywords define procedures that execute every time the property is set or used. The shell code uses a Variant data type, but you want to work with the enumerated data types. Change the Property Get code to this:
Public Property Get ULText() As ULTextEnum ULText = m_ULText End Property
This code only assigns the state property the current member's value, and converting the Variant to the enumeration for the property value's return value is the only necessary change. Change the Property Set code as follows:
Public Property Let ULText(ByVal New_ULText As ULTextEnum) m_ULText = New_ULText ' Test the control's state ' and change the text box accordingly ' (ignore a ULText of 0 which means As Is) If New_ULText = 1 Then Text = UCase(txtULParent.Text) ElseIf New_ULText = 2 Then Text = LCase(txtULParent.Text) End If PropertyChanged "ULText" End Property
The UCase() and LCase() internal functions convert the text value to uppercase or lowercase depending on the value assigned to the property. If the programmer who uses the ActiveX control assigns the ULText property at design time or runtime, this procedure executes. (The next-to-last wizard dialog box gave you a chance to limit the change to design time or runtime, but you kept the default so that programmers will be able to modify the ULText property at runtime or when using the control during an application's design.)
Here's the amazing part: You're through! The next topic section shows you how to compile and test the ActiveX control.
The fun part begins now. Not only can you insert your new control into another application and place the control on the Form window just as you do other controls, but the new ActiveX control takes on every single benefit given to intrinsic controls. Therefore, the control's Properties window will act like other controls. Also, when the programmer who uses the ActiveX control inside a Code window types an assignment to assign a property value, even the Code window's Quick Info pops up to let you select a property. Also, the Toolbox that holds the ActiveX control automatically displays a ToolTip describing the control's name. You'll be proud of your new ActiveX control when you begin using it because the control will act as though it was supplied by Microsoft with Visual Basic!
When you compile the ActiveX control, Visual Basic compiles the control into an .OCX file that you then can insert into a project just as you can other ActiveX controls. The next "Example" and "Next Step" sections demonstrate the steps you take to compile and use the control.
Save your control before compiling it. Choosing Save Project
from the File menu saves both the control and the project.
You can't run an ActiveX control by using the normal F5 keypress
because all ActiveX controls must be compiled before they can
execute. If you attempt to run the control, Visual Basic displays
Figure 31.7's dialog box.
You must compile an ActiveX control and run it from another environment.
To compile the ActiveX control, choose Make from the File
menu. VB displays the Make Project dialog box to request the location
of the compiled ActiveX control. You might want to place the control
in your \Windows\System folder or in a VB work folder that you've
created. (This is the folder you search when you want to load
the ActiveX control into another Visual Basic application's Toolbox
from the Project Properties dialog box.) If the compiler notices
errors, Visual Basic won't build the control and will highlight
the error lines in the code. As soon as you eliminate the bugs,
the compiler returns you to the development environment.
Visual Basic supports two ways to test the control:
Start Visual Basic if you use the Standard Edition. No matter
which edition you use, choose New Project from the File
menu and create a new Standard .EXE file. Press Ctrl+T to open
the Components dialog box. As Figure 31.8 shows, the UL Text Box
control appears at the top of the dialog box.
Your ActiveX control's Project Description text appears in the Components dialog box.
Select the ActiveX control and close the dialog box. The control's bitmap image that you selected when you created the control appears on the Toolbox. To use the control in a simple application, follow these steps:
Private Sub cmdUpper_Click() ' Convert the ULText box to uppercase ' simply by setting its ULText property ULtMyFirst.ULText = Uppercase End Sub Private Sub cmdLower_Click() ' Convert the ULText box to lowercase ' simply by setting its ULText property ULtMyFirst.ULText = Lowercase End Sub
Your control supports all the list box Quick Info help that the other controls provide.
Compile and run the application. Type a value in the UL Text Box control's text box area, using a combination of uppercase and lowercase letters. When you click either command button, the UL Text Box control converts its text to the proper format, as shown in Figure 31.10. Although you didn't need a control to convert the text, the new UL Text Box control supports a built-in conversion property that you can set at any time to convert its contents to uppercase or lowercase letters.
The ActiveX Control's property now performs conversion.
Hour 37, "Packaging Your Application," explains how to distribute the ActiveX controls that you create.
![]()
You've made it through a long ActiveX control example. Although this lesson's ActiveX control was simple and was based heavily on an existing control, the steps required to create the control were numerous. ActiveX control creation is one of the most challenging tasks a Visual Basic programmer can accept. The rewards, however, are an eventual time savings. If you take the time to write an ActiveX control now, you only need to drop that ActiveX control into whatever application uses the control in the future. Also, the market is hot for new ActiveX controls. Just remember: You're not just creating ActiveX controls for Visual Basic programmers, but for all programmers who work on ActiveX control platforms.
In Hour 32, you'll learn the basics of interfacing Visual Basic programs to the Internet.