One of the most exciting features of Visual Basic 5 is the ability to create your own ActiveX controls. No longer are you limited to using the controls created by C/C++ programmers; if you can dream up a great control, you can build it yourself by using Visual Basic. What's even better is that you are not limited to using the controls you create only in Visual Basic. You can use your controls in any application or development tool that can use ActiveX controls. This means that you can turn the tables and begin building ActiveX controls in Visual Basic that C++ programmers will be using in their applications. It's a brave new world for the Visual Basic developer.
This chapter discusses building your own controls by using the latest version of Visual Basic. It covers the various approaches that you can take and some of the issues that you need to take into consideration. The information contained in this chapter is then built upon in later sections, as you learn how to build your own ActiveX controls by enhancing an existing control that "almost" does what you need your control to do.
ActiveX is a technology that was introduced by the Microsoft Corporation in March, 1996. It was not really a new technology, but a renaming of Microsoft's existing OLE technologies. These technologies include the OLE Controls that were introduced with Visual Basic 4 during the previous year. It was the OLE/ActiveX controls that introduced a way to build component objects that can be placed in various applications, including Web pages. This enhanced the ability of programmers to build robust applications quickly with prebuilt components.
NOTE: ActiveX isn't just a new name slapped on an old technology. Along with the new name, Microsoft significantly re-engineered its OLE technologies to make them more "network-friendly." In part, Microsoft removed from the OLE Control (OCX) specification most of the implementation requirements that had added unnecessary overhead to the controls by making them larger and slower than necessary. Along with these changes, Microsoft introduced several new technologies that were aimed specifically for use on the Web, including its Authenticode technology for signing controls and applications. In short, Microsoft put a lot of work into making the evolution of OLE into ActiveX a lot more than just a new name on an old technology.
ActiveX controls can be used in Web pages to add functionality and to greatly improve appearance. As HTML and scripting languages are fairly limited, ActiveX controls have no limitations. Web page designers can interact with the ActiveX controls on their pages with scripting languages such as VBScript.
ActiveX is the next step of the Visual Basic component technology. Visual Basic component technology was started with VBXs, which were used in 16-bit implementations, and were followed by OCXs, which were used in both 16- and 32-bit implementations.
With Visual Basic 5, you can create an ActiveX control as an ActiveX control project. These controls can be used with any container application that supports ActiveX controls. To use an ActiveX control on a Web page, the user's browser must support ActiveX. Microsoft Internet Explorer 3.0 and 4.0 support ActiveX controls. A typical ActiveX control as viewed by IE 3.0 is shown in Figure 24.1.
FIG. 24.1
The ActiveX control on this page allows users to select a specific html page
based upon contents of the combobox.
NOTE: Netscape Communicator, the latest version of Netscape's popular Web browser, now supports ActiveX controls. Previous versions of Netscape's browser still require special plug-ins to accept ActiveX controls.
The basic process of building an ActiveX control is simply a matter of following these steps:
Building a Visual Basic ActiveX control can be as easy or as difficult as you choose. It all depends on whether you can use existing controls in your design, how sophisticated your control's user interface will be, and of course, how much program code you have to write to implement the control's functionality. In any case, there are two basic ways to go about building a control:
These two methods of building a control are listed in the order of general difficulty, although, again, how difficult a control is to create depends on several other factors, as well.
This chapter looks at how to create ActiveX controls from existing controls. The next chapter, "Extending ActiveX Controls," explains how you can add additional functionality to your ActiveX controls.
NOTE: Before you begin building an ActiveX control, you need to decide what type of control it will be. Will your control perform all of its functions without using any nonconstituent controls? Does the control need another control? Will the control be a visual or nonvisual control? These decisions all have to be made before beginning your ActiveX control project.
There are many advantages to assembling an ActiveX control from existing controls. The first is obvious: Because your control's user interface consists of existing controls, you don't have to draw the interface yourself. Another advantage is that your control's users will probably already be familiar with the controls that make up your new control. This familiarity makes your new control easier to use.
Still another advantage is that, when you add an existing control to your new custom control, you get the existing control's complete functionality, too; an important consideration when you consider how many event procedures, methods, and properties are supported by a standard control.
You can often use third-party controls as constituent controls, but usually you'll use Visual Basic's standard controls or intrinsic controls. The Visual Basic intrinsic controls are shown in Table 24.1.
Icon |
Name | Description |
|
CheckBox | A small button-like control that the user can check or uncheck. |
|
ComboBox | A control that is comprised of a scrollable list containing valid selections and an edit box into which the user can type selections. The user can also use the mouse to choose selections from the list. |
|
CommandButton | A typical push-button type of control. |
|
Data | A control that you can link to database fields. |
|
DirListBox | A control that displays the directories on the current drive. |
|
DriveListBox | A control that displays the drives on the system. |
|
FileListBox | A FileListBox displays the files in the current directory. |
|
Frame | A control that enables you to place controls into a group, by providing an outline to enclose the group and a caption to identify the group. |
|
HScrollBar | A control that represents a horizontal scroll bar. |
|
Image | A control that displays an image. |
|
Label | A control that holds a static (unchangeable) line of text. |
|
Line | A control that enables you to draw lines on a form or control. |
|
ListBox | A control that features a scrollable list from which the user can make a selection. Similar to a ComboBox, but without the edit box. |
|
OptionButton | A small, circular, button-like control that the user can use to toggle options on or off. |
|
PictureBox | A control that's similar to an Image control, but which features more methods. |
|
Shape | A control you can use to draw various types of shapes on a form or control. |
|
TextBox | A control that represents an editable line of text. |
|
Timer | A control that's used in Visual Basic projects to access and control Windows timers. |
|
VScrollBar | A control that represents a vertical scroll bar. |
A big advantage of using the intrinsic controls as constituent controls is that you don't need to acquire additional licenses to distribute the controls with your programs. The intrinsic controls are built in to the Visual Basic runtime files that you always need to distribute. If you use third-party controls, you almost certainly have to pay licensing fees. (For more information on licensing, see Chapter 25, "Extending ActiveX Controls.")
The following sections take a look at how you can use constituent controls to build an entirely new control. The address control that you create can itself become a constituent control for future ActiveX controls; in this way, a complete library of controls can be built up quite rapidly and without a lot of coding.
Now that you have some background information on using constituent controls, you can get started creating your first full-fledged ActiveX control. The control that you'll build in the following sections enables users to enter their name and address into a predefined form. To create the control, you'll use Visual Basic Label and TextBox controls as constituent controls. To build this control, follow these steps:
FIG. 24.2
ActiveX controls can contain all of the controls that a regular application
has.
4. Add five Labels to the form and give them the following properties:
Name Caption |
lblName "Name:" |
Name Caption |
lblStreet "Street:" |
Name Caption |
lblCity "City:" |
Name Caption |
lblState "State:" |
Name Caption |
lblZip "Zip:" |
5. Add five Text boxes to the form with the following properties.
Name | txtName |
Name | txtStreet |
Name | txtCity |
Name | txtState |
Name | txtZip |
6. Click the UserControl object and resize it so that the constituent controls fit neatly inside. You can resize the control by using the sizing handles or by changing the Height and Width properties in the Properties window to 3090 and 3810, respectively. The completed form will now look like that shown in Figure 24.3.
FIG. 24.3
The full set of constituent controls should look like this.
The next step is to add code that responds to the Resize event, which occurs whenever a control is created or resized.
To add program code for the Resize event, first double-click the UserControl object to display the control's code window. When the window pops up, it displays the UserControl_Initialize() event procedure. In the Procedures box, select Resize, and the UserControl_Resize() event procedure appears in the code window. Add the lines shown in Listing 24.1 to the UserControl_Resize() event procedure.
` Don't let the developer make the ` height of the control too small. If UserControl.Height < 3090 _ Then UserControl.Height = 3090 ` Don't let the developer make the ` width of the control too small. If UserControl.Width < 1500 _ Then UserControl.Width = 1500 ` Change the width of the controls ` to fit into the resized UserControl. txtName.Width = ScaleWidth - 500 txtStreet.Width = ScaleWidth - 500 txtCity.Width = ScaleWidth - 500 txtState.Width = ScaleWidth / 2 - 400 txtZip.Width = ScaleWidth / 2 - 400 ` Reposition the Zip Code controls. lblZip.Move ScaleWidth / 2 + 160 txtZip.Move ScaleWidth / 2 + 160
After adding the previous lines to the UserControl_Resize() event procedure, be sure to save your changes by clicking the Save Project Group button on VB5's toolbar or by choosing File, Save Project Group.
When a developer (who uses the control to create an application or Web page) resizes the Address control, be sure that the control's interface still looks okay. For this reason, you don't want the height of the Address control to get smaller because then there won't be room for all the Label and TextBox constituent controls. (Okay, if you really want to, you can use smaller fonts, but who wants to go to all that trouble?) So, the first thing UserControl_Resize() does is make sure that the control's height doesn't get set to less than 3090:
If UserControl.Height < 3090 _ Then UserControl.Height = 3090
NOTE: The height of 3090 is an arbitrary number. For the sake of this example, a height of 3090 was chosen. Your control height may be less than or greater than this size.
Now, if the developer tries to make the control too small, UserControl_Resize() will set the height back to where it belongs. The developer can make the control taller, but he can't make it shorter.
NOTE: Notice how you can change the value of a property with a line of code such as Object.Property = Value, where Object is the object whose property you want to change, Property is the name of the property to change, and Value is the value to which you set the property. Notice also that you separate the object and property names with a period.
Although you have to be careful about how the developer changes the control's height, the control's width has a little more flexibility. Because TextBox controls can scroll text, the TextBox doesn't necessarily have to be wide enough to hold the entire line that the user types in. So, your new control can allow the developer to change the width of the Address control. Still, you want to limit the width to a sensible amount. In Listing 24.1, that limit is 1500, which is enforced like this:
If UserControl.Width < 1500 _ Then UserControl.Width = 1500
NOTE: As with the height, the width of 1500 is an arbitrary number. For the sake of this example. a width of 1500 was chosen. Your control width may be less than or greater than this size.
Because you can never know how the developer has set the Address control's size, you need to size and position the constituent controls every time a Resize event occurs. The first step in this task is to set the constituent controls' widths, like this:
txtName.Width = ScaleWidth - 500 txtStreet.Width = ScaleWidth - 500 txtCity.Width = ScaleWidth - 500 txtState.Width = ScaleWidth / 2 - 400 txtZip.Width = ScaleWidth / 2 - 400
ScaleWidth and ScaleHeight hold the width and height of the UserControl object's visible area. In the first three lines of the previous code segment, the code sets the width of the constituent controls to 500 twips less than the width of the UserControl object's visible width. The width of the txtState and txtZip controls is a little trickier to set because these controls are on the same line. To fit these TextBoxes properly, the code first divides ScaleWidth by 2, giving the total amount of space each TextBox can have. Then the code subtracts 400 twips to put a little space between the controls.
Because the controls for the State and Zip fields are on the same line, when the UserControl object changes width, the Zip controls (lblZip and txtZip) have to be repositioned. The UserControl_Resize() event procedure takes care of that little detail like this:
lblZip.Move ScaleWidth / 2 + 160 txtZip.Move ScaleWidth / 2 + 160
NOTE: You can use an object's Move() method to reposition the object. A call to Move() looks like object.Move left, top, width, height, where object is the object to move, left is the position of the object's left edge, top is the position of the top edge, width is the object's new width, and height is the new height. Only the left argument is required; that is, top, width, and height are optional arguments.
You've created your control's interface and added code to handle one important event. You're now ready to see Address in action, by opening the test application's designer window and adding an instance of the Address control. At this point, you'll be playing the role of an application or Web page developer. In this role, you want to see how the control will act when another developer gets his hands on it.
After you have entered all the code for the control, you are ready to test the control. A good way to test your ActiveX control is in a control project. To test the Address control, follow these steps:
If you have problems with the control, you can use the same debugging techniques to find problems in controls that you used to find problems in standard programs. You can set break points and step through the code line by line, whether in the ActiveX Control project or in the Standard EXE project. (See Chapter 25, "Extending ActiveX Controls," for more information on debugging your code.)
As you can see, the Address control looks exactly as you designed it. That's because the first time the instance appears in the test application's form, it uses the width and height of the control as you set it when you designed the control. The sizes and positions of the constituent controls are handled in the UserControl_Resize() event procedure, based on the current size of the Address control.
To really see UserControl_Resize() in action, reduce the width of the Address control. When you do, the constituent controls automatically resize themselves according to the new Address size. If you try to reduce the height of the Address control, the control springs back to its minimum size. You can, however, enlarge the control as much as you like. If you reduce the width of the control as far as it'll go, you end up with something like Figure 24.4. No matter how narrow you try to make the control, it'll always stay at least at its minimum size.
Fig. 24.4
This is the test application when the Address control is at its minimum width.
After you've created and tested your control, you need to compile it into an .OCX file, which is the stand-alone binary version of the control that you can distribute. When you compile your control into an .OCX file, developers can install the control on their systems and then use the control in their own projects, regardless of whether they're working with a programming language, a Web-page authoring application, or some other development tool.
To create a stand-alone. OCX file for the Address control, perform the following steps:
After completing the previous steps, your project group will contain only the test application, AddressTestApp. However, if you look at Visual Basic's Toolbox, you'll see that the Address control is again available. Now, however, Visual Basic will use the compiled control rather than the version that was originally part of your project group.
NOTE: If you need to, you can easily add the Address control project back to the control group from which you deleted it. Just select the File, Add Project command from VB5's menu bar, and then select the project from the Existing page of the Add Project property sheet.
The Address control is one of those controls that can easily be used as the basis for other ActiveX controls. One possible use for the Address control is to provide a consistent look and feel to all of your applications that require user data entry. Another possible use is in a Web page to collect data in a guest book control.
The Address control is by no means a complete and robust control. The next few sections examine how methods and events can be added to your control. Chapter 25, "Extending ActiveX Controls," provides some ways to make your control more robust and bulletproof.
See "Control Error Handling," Chapter 25
In the previous example, you built an ActiveX control out of constituent controls. You added some code to one of the events and successfully tested the control in a test project. However, you did not add any additional methods or events. You used only those methods and events that were supported by your constituent controls. In the present example, you build an ActiveX control and provide some methods and events.
This ActiveX control has three constituent controls: two command buttons and one label. In this example, you toggle the caption of the label by clicking the two command buttons. To build this control, follow these steps:
FIG. 24.5
Draw two command buttons on the control drawing area by selecting the CommandButton
icon and then clicking and dragging on the drawing area.
Private Sub cmdYes_Click() lblDisplay.Caption = "Yes" End Sub
Private Sub cmdNo_Click() lblDisplay.Caption = "No" End Sub
FIG. 24.6
Once you close the window containing the control you are developing, it becomes
available in the Toolbox for use in any other Visual Basic application.
FIG. 24.7
When you run the standard Visual Basic project, you can verify that your control
works correctly.
FIG. 24.8
To build the necessary files for use in an HTML document, you need to use
the Application Setup Wizard to create an Internet Download Setup.
FIG. 24.9
You can specify whether the Visual Basic runtime files (and other necessary
files) will be downloaded from the Microsoft Web site, the same Web site as your
control, or a third Web site.
FIG. 24.10
By marking your control as safe for initialization and scripting, you place
your guarantee that your control can't harm the user's computer, even if used in
HTML documents that you didn't build.
FIG. 24.11
The File Summary dialog box shows which files will be included in the Internet
download files that will be packaged for inclusion on a Web site.
You can make properties, methods, and events of your ActiveX controls available to Web designers to increase your controls' flexibility. After properties, methods, and events are exposed, they are available to be manipulated from script code on an HTML page, such as VBScript. You can make the native properties, methods, and events of your constituent controls available, or you can make up your own properties, methods, and events for the special functionality that you are trying to achieve with your control. You need to be careful in what you enable, as it might make your control unsafe.
See "Marking Your Controls Safe for Scripting and Initialization," Chapter 25
FIG. 24.12
After the Setup Wizard has completed building the download files, it creates
a simple HTML file you can open in Internet Explorer to test your control.
Properties are characteristics of your controls. By changing properties, you can change the appearance and behavior of ActiveX controls. By exposing your ActiveX control's properties, you allow Web developers to manipulate your control. The availability of your ActiveX control's properties is controlled by property procedures. A property procedure is a public procedure that makes your property available to the outside world. Property procedures allow you to make properties read, write, or read and write.
You need to implement two property procedures for all properties that you want to make available to the user of your control. These two property procedures are the Get and Let procedures. The property Get procedure allows the current value of the property to be read by programming code or script, whereas the property Let procedure allows the current value of the property to be changed by the code. These methods also allow the property to appear in the Properties window when using the control in a Visual Basic application.
In the following example, you make the UserControl object that you made in the first example have a public property named BackColor, which will be available for read and write. To make a property of your control available to be read from script, make a property Get procedure. The name of the procedure is BackColor, and it is of type OLE_Color. It returns the value of the UserControl, as seen in Listing 24.4.
Public Property Get BackColor() As OLE_COLOR BackColor = UserControl.BackColor End Property
To make a property available to be changed from script, use a property Let statement, as in Listing 24.5.
Public Property Let BackColor(ByVal New_BackColor As OLE_COLOR) UserControl.BackColor() = New_BackColor PropertyChanged "BackColor" End Property
To the Web designer, your ActiveX control should look like one object, although it might be made of many constituent controls. For that reason, a Web designer should have to change only one property to change one attribute--for example, there should be only one BackColor property. You can change multiple objects on your control's properties with one property procedure. For example, if you want the BackColor of the UserControl and the label to have the same BackColor, use one property procedure and have it modify two properties, as in Listing 24.6. This enables you to change both BackColor properties with the single property.
Public Property Let BackColor(ByVal New_BackColor As OLE_COLOR) UserControl.BackColor() = New_BackColor lblDisplay.BackColor() = New_BackColor PropertyChanged "BackColor" End Property
Methods give Web designers the ability to perform actions on the objects of their ActiveX controls. A method is just a function or sub that is declared as public. The function or sub's name associates with the name of a method of an object. For instance, you can use the code in Listing 24.7 to add a method to the AXYesNo control, which can then be used to set the label.
Public Sub SetText(Item As String) lblDisplay.Caption = Item End Sub
By exposing events, you give Web designers the ability to call the code that is associated with those events.
To expose an event, you first declare the event's name in the General Declarations section of your UserControl object. Use the keyword Event and then the events name, followed by parentheses--for example, Event Click() declares there will be a Click event.
Second, create a procedure that uses your new event by using the code in Listing 24.8.
Private Sub cmdNo_Click() `Change the caption lblDisplay.Caption = "No" `Raise the Click event RaiseEvent Click End Sub
The previous sections introduced the concepts involved in creating ActiveX controls, including how to place controls on a User Control Window, and how to create an entirely new control by adding properties, methods, and events. However, you can also create "new" controls simply by adding capabilities to an existing control. This means you will be working with a single base control, but adding properties, methods, and events to provide additional capabilities to the user. For example, you might want a text box that accepts only certain characters, or perhaps a scroll bar that works with a range of letters rather than numbers. Placing the code that performs these tasks into an ActiveX control makes it easier to use the code in future programs. For example, rather than adding special code to every TextBox control in your program, you simply use your "enhanced" control in place of the text box.
To create these enhanced controls, you use many of the same techniques that you have already learned. However, you also can use a Visual Basic Wizard to make quicker work out of the process, which is explained later in the chapter.
To create an enhanced control, follow these five basic steps:
The following sections walk you through these steps, using a text box as the base control. Your "enhanced" text box will have a property that allows the programmer to choose a set of acceptable characters that the user can enter. This control will be called TxtCharLimit (short for "Limited Character TextBox"). It will be just like the standard TextBox but with one additional property, CharAccept, which will allow the user to choose either all characters, just letters, or just numbers.
The steps to create the enhanced text control are very similar to the steps you used to create the Address and AXYesNo controls. For the enhanced text control, these steps are as follows:
Item | Setting |
Project Type | ActiveX Control |
Project Name | TextLimited |
Project Description | Text Box for Limited Character Set |
User control Name property | TxtCharLimit |
User control Public property | True |
When you have completed setting up the user interface of the enhanced text control, it should look like the one in Figure 24.13.
You also need to set up the Resize event procedure of the User Control to make the text box fit the space that is drawn by the developer when using your control. This Resize event procedure is shown in Listing 24.9.
FIG. 24.13
A simple text control can be enhanced with other capabilities.
Private Sub UserControl_Resize() txtCharSet.Height = UserControl.ScaleHeight txtCharSet.Width = UserControl.ScaleWidth End Sub
The simple two-line Resize event procedure is all the code necessary for the user interface of your sample control. Its purpose is to keep the text box the same size as the UserControl object. Before moving on, test it by performing the following steps:
The purpose of jumping the gun like that is to get you used to the idea that the code in your ActiveX control does not have to be explicitly executed. Remember, when developing an ActiveX control, the code you write is used at design time in the host program.
For now, remove the Standard EXE project by right-clicking it in the Project Explorer Window and then choosing Remove Project1. Now it is time to work on the enhancements to the control.
The enhancement you are going to make to the TextBox control is to tell it whether it should accept any characters, just letters, or just numbers. You accomplish this by adding your own property, called CharAccept, which can have one of the following three values:
The first thing you need is a private variable to store the property value internally. To do this, create the variable in the General Declarations section of the UserControl object:
Private mCharAccept As Integer
Next, you need to create the new property, called CharAccept. You can do this either by typing in the Let and Get procedures by hand, or by choosing Tools, Add Procedure, Property. The code for the CharAccept property is fairly easy to understand. When the developer needs the value of the CharAccept property, the Property Get procedure simply passes what is stored in the private variable. When the value of the CharAccept property is set, the Property Let procedure assigns one of the valid values to the private variable. The following is the code for both Property procedures:
Public Property Get CharAccept() As Integer CharAccept = mCharAccept End Property Public Property Let CharAccept(ByVal nNewValue As Integer) Select Case nNewValue Case 1 To 2 mCharAccept = nNewValue Case Else mCharAccept = 0 End Select PropertyChanged "CharAccept" End Property
Notice the use of the PropertyChanged method. This method works with the ReadProperties and WriteProperties events, which you see in the next few paragraphs.
You now need to use the InitProperties event of the user control to specify an initial value of the property, as follows:
Private Sub UserControl_InitProperties() mCharAccept = 0 End Sub
This code makes sure that a value is set, even if the developer does not set it.
You also need to create the code for the WriteProperties and ReadProperties events to preserve the design time settings of CharAccept. These two events use the PropertyBag object to save and retrieve the value of the CharAccept property. The PropertyBag object enables you to maintain the design environment value of CharAccept. The code for these two events, shown in Listing 24.10, is not hard to understand. What is important, however, is why you need the code.
Private Sub UserControl_ReadProperties(PropBag As PropertyBag) mCharAccept = PropBag.ReadProperty("CharAccept", 0) End Sub Private Sub UserControl_WriteProperties(PropBag As PropertyBag) PropBag.WriteProperty "CharAccept", mCharAccept, 0 End Sub
Remember that an ActiveX control's code starts executing the moment you draw it on a form. Suppose you set the value of a property during design time. In your sample control, assume you set the value of CharAccept to 1. You also may change it several times while your program is running. The normal behavior for a control is to revert to its original design-time values when the program ends, thus adding the requirement of maintaining two separate states of the property.
More simply put, if you change a property at design time, the control has to know to get this new value rather than use the default. Conversely, if the property's value is changed during program execution, the control has to retrieve the value when it returns to the design state.
The PropertyBag object allows your ActiveX control to store properties about itself, making this behavior possible. The PropertyChanged method provides notification that the user has changed a property. By knowing the state of the program and whether the PropertyChange method has been invoked, VB can fire the WriteProperties and ReadProperties events.
The next step in your sample project is to create the code that makes it do something different than a normal text box. In this case, you use the text box's KeyPress event to scan each character as it is entered. Visual Basic will pass the ASCII code of the characters through the event's KeyAscii parameter. Depending on the ASCII code and setting of CharAccept property, you will either accept the character or set KeyAscii to 0, which causes the text box to not display the character.
In addition to not displaying the character, you want to inform the host program that the user has entered an invalid character. You do this by creating an event called UserError. To create this event, add the following line of code to the General Declarations section of the UserControl object:
Public Event UserError()
This event works like an event in any other control; someone using your control can place code in it. The only thing you have to do is fire the event by using the RaiseEvent method.
Because there are three sets of acceptable characters, you can use a Select statement to handle the choices. One other item of note--you need to enable the backspace key (ASCII code 8) in any of the character sets that you use. Otherwise, the user won't be able to delete the previous character. The code for the KeyPress event is shown in Listing 24.11.
Private Sub txtCharSet_KeyPress(KeyAscii As Integer) If KeyAscii = 8 Then Exit Sub Select Case mCharAccept Case 0 `Any character is acceptable Exit Sub Case 1 `Only numbers may be entered If KeyAscii >= 48 And KeyAscii <= 57 Then Exit Sub Else KeyAscii = 0 Beep RaiseEvent UserError End If Case 2 `Only letters may be entered If KeyAscii >= 65 And KeyAscii <= 90 Then Exit Sub ElseIf KeyAscii >= 97 And KeyAscii <= 122 Then Exit Sub Else KeyAscii = 0 Beep RaiseEvent UserError End If End Select End Sub
The code in this listing is fairly simple. KeyAscii represents the typed character, which is checked for validity by Select Case and If statements. If the character falls outside an acceptable range, the control beeps and raises the UserError event.
After you have entered all of the code for the control, you are ready to test the control. To test the TxtCharLimit control, follow these steps:
If you have problems with the control, you can use the same debugging techniques to find problems in a control as you do for finding problems in standard programs. You can set break points and step through the code line by line, whether in the ActiveX Control project or in the Standard EXE project. (See Chapter 8, "Programming Visual Basic," for more information on debugging your code.)
One thing you may have noticed by now is that all the custom controls you create have the same symbol in the Toolbox. This can cause a lot of confusion if you are working with multiple controls. Although the ToolTips provide a description of the control, it is better to have a custom icon to identify each control. You can do this by setting the value of the ToolboxBitmap property of the user control. This property determines what is displayed in the Toolbox for your control. If the property is set to None, the default icon is used. You can set the property to any bitmap, but be aware that the Toolbox icon is only 16 x 15 pixels. Therefore, you should use custom bitmaps that are created in that size.
When you created the CharAccept property of the enhanced text box, you created one piece of the public interface of the control. However, your users probably will also want to be able to access most of the standard properties, methods, and events of the text box. For example, you may have noticed that the Text property was not accessible from your custom control. This makes sense because you did not add any code for it. The section "Exposing Properties of ActiveX Controls" exposed properties with the same name as the component property to allow the user to access these properties. Because there were only a few properties, each property was created by hand. However, you can imagine that handling this for the dozens of properties of a control could get very tedious.
Fortunately, Visual Basic provides a tool to make this process much easier--the ActiveX Control Interface Wizard. First you tell the Wizard the names of all the properties that you want to have for your control. It then enables you to "bind" the properties of your control to the properties of a component of your control. The end result is that the Wizard generates the appropriate code for you.
The first step to using the VB ActiveX Control Interface Wizard is adding it to your design environment. For this purpose, Visual Basic includes the Add-In Manager. To start the Add-In Manager, choose Add-Ins, Add-In Manager. This opens the Add-In Manager dialog box
To add the VB ActiveX Control Interface Wizard, click the box next to the name of the Wizard. This places a check mark in the box, indicating that the Wizard will be part of your desktop. Next, click the OK button to exit the Add-In Manager and add the Wizard to Visual Basic.
Next, you need to re-create the Limited text control by using the Wizard. To begin, start a new ActiveX Control project and draw a text box on the UserControl window. Set up the names and sizes the same way you did in the previous example. Next, start the Wizard by choosing the ActiveX Control Interface Wizard item from the Add-Ins menu. The Wizard will start by displaying the initial screen, shown in Figure 24.14. You then click the Next button to start the actual work of setting up your properties.
FIG. 24.14
The ActiveX Control Interface Wizard simplifies the process of creating properties
by creating much of the code for you.
NOTE: For the Wizard to work most effectively, you must add all the required components to the user control before starting the Wizard.
The next step in using the Wizard is to select the properties, methods, and events that you want to make available to your control. Collectively, properties, methods, and events are referred to as members. On page two, shown in Figure 24.15, the Wizard contains a list of the names of just about every item that you could find in any control in Visual Basic. To select a property or method to create, highlight the name of the item in the Available names list, and then click the right arrow button to select the item. For your sample control, highlight the Text property on the left and click the right arrow button.
FIG. 24.15
Select properties, methods, and events for your control from the Available
names list.
TIP: You can also select an item by double-clicking it in the list.
After you add the Text property to the Selected Names list, click the Next button to move to the next page of the Wizard.
NOTE: You may have noticed that the selection of properties works just like the two-column pick list control that was created in Chapter 9, "Using the Windows Standard Controls."
After selecting the predefined properties, methods, and events for your control, you are taken to the page of the Wizard where you can enter the new custom items for your control. The Create Custom Interface Members page of the Wizard contains a list of all the custom members that will be created for your control.
If you have previously defined public properties or other members, they will appear in this list when you first access the page. From this page of the Wizard, you can add new members, or edit or delete existing ones. To add a new member, click the New button of the Wizard. This opens the Add Custom Member dialog box. In this dialog box, specify the name of the member and its type. Create the CharAccept property by typing CharAccept in the Name field, and then clicking OK.
While you are on this step, create the UserError event. Then click the Next button to move on.
CAUTION: It is advisable not to edit members of the control that you previously defined with code alone. This is because the Wizard works by analyzing comments it places in the code. The Wizard may or may not be able to correctly interpret your hand-typed code.
The next step in the Wizard is to assign the public members of the custom control to members of the constituent controls. This process is referred to as mapping the members. For example, rather than creating your own Text property, you can simply map the Text property of your custom control to the Text property of txtCharSet. The Set Mapping page of the Wizard, shown in Figure 24.16, contains a list of all the properties, methods, and events that you identified as being part of the public interface of the custom control.
FIG. 24.16
Mapping the public members of the control gives you a direct link to items
in the constituent controls.
The Set Mapping page contains two combo boxes for identifying the control and the control's member to which a public member should be mapped. To map a single public member of the custom control, first select the member in the Public Name list. Go ahead and highlight the Text property on the left. Next, select txtCharSet from the Control drop-down list. (This list contains the names of all the components in your custom control.) After selecting the component, you can select the member of the component from the Member drop-down list. In this case, the Text property will be selected automatically for you. This process is illustrated in Figure 24.17 for the Text property of the TxtCharLimit control.
FIG. 24.17
Select all the public members for the TxtCharLimit control and let
the mapping occur automatically.
Note that with this screen, you can map more than one public member at a time. The list of public names supports multiple selections. You can select multiple members from the list, then select a component to which to map the members. Each public member will be mapped to the property or method of the component that bears the same name. For example, the Text property of the custom control would automatically map to the Text property of a text or combo box.
After you have mapped the Text property, click the Next button to proceed to the final page of the Wizard. This final page, shown in Figure 24.18, lets you set the attributes of each public member that is not mapped to a constituent control.
Depending on the type of member you are creating, the Wizard allows you to specify different attributes. For a property, you can specify the type of data the property will hold, the default value of the property, and what type of access the user has to the property at design time and run time. The access type determines if a Property Let, Property Get, or both procedures are created for the property. For Run Time access, you can choose Read/Write, Read Only, Write Only, or none. For Design Time access, you can choose Read/Write, Read Only, or none.
For your sample control, set the Data Type of the CharAccept property to Integer and set the default value to 0. You can also type an optional description in the Description box. Property descriptions appear at the bottom of the Properties window during design time. Because this is the final step of the Wizard, you now are ready to click the Finish button.
FIG. 24.18
Set the attributes for properties and methods before completing the code for
the control.
After you click the Finish button, the ActiveX Control Interface Wizard creates a number of code modules in your control. The Wizard also displays a summary page, providing you with details of the steps remaining to finish your control.
After reviewing the information on the summary page, you can take a look at the code that was generated by the Wizard. You can view the code by clicking the user control in the Project window and then clicking the View Code button. This opens the Code window containing the code that the Wizard created for you.
Take a look at some of the pieces of the code that is generated. First, in the General Declarations section, you will find that the Wizard has created constants for the default values of any unmapped properties. The CharAccept property has been given a default value of 0. In addition, the Wizard has automatically created the private property variable CharAccept. This is followed by the declaration of any events that you requested to be included in the control. A sample of this code is shown in Listing 24.12. If you mapped any events, you will notice that the Wizard places comments in the code that indicate how events are mapped to component events.
Option Explicit `Default Property Values: Const m_def_BackColor = 0 Const m_def_ForeColor = 0 Const m_def_CharAccept = 0 `Property Variables: Dim m_BackColor As Long Dim m_ForeColor As Long Dim m_CharAccept As Integer `Event Declarations: Event Click() `MappingInfo=txtCharset,txtCharset,-1,Click Event DblClick() Event UserError() Event KeyPress(KeyAscii As Integer)
The declarations of variables and events are followed by the property procedures. These procedures are created for properties that are mapped to a component property and those that are custom properties. As seen in Listing 24.13, the code for the Text property is complete, whereas the CharAccept property has a skeleton function ready for you to finish.
`WARNING! DO NOT REMOVE OR MODIFY THE FOLLOWING COMMENTED LINES! `MappingInfo=txtCharset,txtCharset,-1,Text Public Property Get Text() As String Text = txtCharset.Text End Property Public Property Let Text(ByVal New_Text As String) txtCharset.Text() = New_Text PropertyChanged "Text" End Property Public Property Get CharAccept() As Integer CharAccept = m_CharAccept End Property Public Property Let CharAccept(ByVal New_CharAccept As Integer) m_CharAccept = New_CharAccept PropertyChanged "CharAccept" End Property
Note that if you had requested any methods, the Wizard would create them as Functions, instead of Sub procedures.
Finally, the code to read and write property values to the PropertyBag object is generated automatically. This code is illustrated in Listing 24.14.
`Load property values from storage Private Sub UserControl_ReadProperties(PropBag As PropertyBag) m_BackColor = PropBag.ReadProperty("BackColor", m_def_BackColor) m_ForeColor = PropBag.ReadProperty("ForeColor", m_def_ForeColor) txtCharset.Text = PropBag.ReadProperty("Text", "") m_CharAccept = PropBag.ReadProperty("CharAccept", m_def_CharAccept) End Sub `Write property values to storage Private Sub UserControl_WriteProperties(PropBag As PropertyBag) Call PropBag.WriteProperty("BackColor", m_BackColor, m_def_BackColor) Call PropBag.WriteProperty("ForeColor", m_ForeColor, m_def_ForeColor) Call PropBag.WriteProperty("Text", txtCharset.Text, "") Call PropBag.WriteProperty("CharAccept", m_CharAccept, m_def_CharAccept) End Sub
To complete the coding of your control, you need to add the custom code that is required for the CharAccept property and KeyPress event because the Wizard created only a skeleton for these items. After all, if the Wizard could do everything, then you would be out of a job!
You have seen Property Pages used for some of the controls that come with Visual Basic. These dialog boxes make it easy for you to set the properties of a control by organizing them into groups. You can create property pages for your own custom controls by using the Property Pages Wizard. Like the ActiveX Control Interface Wizard, you have to add the Property Pages Wizard to the desktop by using the Add-In Manager. After this is done, you can access the Wizard from the Add-Ins menu of Visual Basic. As you start the Property Pages Wizard, you are shown an introductory screen that explains the purpose of the Wizard. Clicking the Next button on this page takes you to the first page, where the real work is done.
The first page of the Wizard lets you define the pages of the Property Pages dialog box (see Figure 24.19). If you have included Font and Color properties in your control, the Wizard starts out with two default pages--StandardColor and StandardFont. If you do not need these pages, just click the box next to the name to remove them from your Property Pages.
In addition to the default pages, you can add new pages to the dialog box. Clicking the Add button brings up the Property Page Name dialog box, which is an input box where you can enter the name of the page to create. As you add a page name, it is placed in the list of available pages and is automatically checked. The order of the page names in the list is the order in which the tabs will appear in your Property Pages dialog box. You can change the order by selecting a page and using the arrow keys to move it within the list.
FIG. 24.19
Create new pages or rename old ones in the Property Pages Wizard.
When you have finished adding pages to the dialog box, click the Next button to move to the next page of the Wizard.
The next step in creating your Property Pages is to add the appropriate properties to each page of the dialog box. The Add Properties page of the Property Pages Wizard is shown in Figure 24.20.
FIG. 24.20
The Add Properties page displays a list of available properties and shows
the defined pages of the dialog box.
To add a property to a page, click the tab corresponding to the page where you want the property placed, and then select the property from the Available Properties list and click the right arrow button. Notice in Figure 24.20 the addition of a General property page and the inclusion of the CharAccept property. In addition, if you have the default pages of StandardColor and StandardFont, you will notice that the appropriate properties have already been added to these pages.
TIP: You can drag and drop a property onto a tab to place it on the corresponding page of the Property Pages.
When you have finished adding properties to the pages, click the Finish button to complete the creation of your Property Pages. As with the ActiveX Control Interface Wizard, the Property Pages Wizard shows you a summary page that provides additional information to complete your custom control.
To use the Property Pages you created, you need to add an instance of your custom control to a project. Then, in the Properties window, click the ellipsis button next to the Custom property. Then, just like the Property Pages of other controls, your Property Pages dialog box appears to allow the user to customize the control. A sample of a custom control's Property Pages is shown in Figure 24.21.
FIG. 24.21
Your Property Pages help users set up your custom control.
TIP: You can also access the Property Pages by right-clicking the control and then selecting the Properties item from the context menu.
Now that you have seen the general concepts for enhancing controls, you are ready to begin creating your own enhanced controls. One idea to get you started is a scroll bar that lets you select letters rather than numbers. This scroll bar lets the user set the upper and lower range of letters for the bar, and to set whether the letters returned by the Value property are uppercase or lowercase. Figure 24.22 shows the use of the scroll bar in a program, and Listing 24.15 shows the code required to make the scroll bar work.
FIG. 24.22
Use a scroll bar to enter letters.
`Default Property Values: Const m_def_Max = 26 Const m_def_Min = 1 Const m_def_Value = 0 Const m_def_ReturnCase = 0 `Property Variables: Dim m_Max As Integer Dim m_Min As Integer Dim m_Value As Integer Dim m_ReturnCase As Integer Private Sub hsbLetter_Change() RaiseEvent Change End Sub Public Property Get Max() As String m_Max = hsbLetter.Max Max = Chr(m_Max + 64) End Property Public Property Let Max(ByVal New_Max As String) m_Max = Asc(UCase(New_Max)) - 64 hsbLetter.Max = m_Max PropertyChanged "Max" End Property Public Property Get Min() As String m_Min = hsbLetter.Min Min = Chr(m_Min + 64) End Property Public Property Let Min(ByVal New_Min As String) m_Min = Asc(UCase(New_Min)) - 64 hsbLetter.Min = m_Min PropertyChanged "Min" End Property Private Sub hsbLetter_Scroll() RaiseEvent Scroll End Sub Public Property Get Value() As String m_Value = hsbLetter.Value If m_ReturnCase = 0 Then Value = Chr(m_Value + 64) Else Value = Chr(m_Value + 96) End If End Property Public Property Let Value(ByVal New_Value As String) m_Value = New_Value m_Value = Asc(UCase(New_Value)) - 64 hsbLetter.Value = m_Value PropertyChanged "Value" End Property Public Property Get ReturnCase() As Integer ReturnCase = m_ReturnCase End Property Public Property Let ReturnCase(ByVal New_ReturnCase As Integer) If New_ReturnCase > 1 Then New_ReturnCase = 1 If New_ReturnCase < 0 Then New_ReturnCase = 0 m_ReturnCase = New_ReturnCase PropertyChanged "ReturnCase" End Property `Initialize Properties for User Control Private Sub UserControl_InitProperties() m_Max = m_def_Max m_Min = m_def_Min m_Value = m_def_Value m_ReturnCase = m_def_ReturnCase End Sub
Another idea for an enhanced control is a type-ahead combo box. This type of combo box keeps the items in the drop-down list sorted and then performs a search on the item as the user types letters in the edit portion of the box. This type of feature makes the combo box easier to use for data entry.
This chapter has shown you how to enhance existing controls to create new controls, and how to use the ActiveX Control Interface Wizard and Property Pages Wizard to make your control-creation work easier. To learn more about other topics covered in this chapter, see the following:
© Copyright, Macmillan Computer Publishing. All rights reserved.