Chapter 10

Window Dressing


CONTENTS

This chapter covers a mixed bag of ActiveX control features. Many of these features are cosmetic in nature, but even the seemingly superficial features can be crucial if you want to give your control the feature set and interface that users expect.

This chapter is chock-full of demonstrations that show how each feature works; many of the demonstrations include information on non-obvious techniques you can use to achieve a number of interesting effects in your ActiveX control project.

Giving Your Control a Transparent Background

Enabling your control to show the background of its container is a feature that's particularly useful when your control is based on several constituent controls.

You can make the background of your UserControl property transparent by setting its BackStyle property to 0 - Transparent. Bear in mind that making the background of your control transparent creates a significant performance hit on your control because it must do much more calculation in order to redraw itself.

Design Time, Runtime, Read-Only Properties

Although some properties are design-time-only or runtime-only, most properties can be set at any time. The nature of each property will help you determine whether your control should expose properties that can't be set at runtime or design time. You should obviously avoid restricting user access to properties if you can help it.

You can make a property design-time-only or runtime-only by inspecting the UserMode property of the Ambient object in the Property Let procedure. If the UserMode property is True, then it is runtime. If the property is False, the control is in design mode.

Demonstration

Here's a demonstration of a property that can't be set at runtime.

  1. Create a new control project. Give the UserControl the name ArkeyMalarkey.
  2. Enter the following code in the ArkeyMalarkey control's code window:
' Declarations

Private mlngArkeyMalarkey As Long


Public Property Get ArkeyMalarkey() As Long
    ArkeyMalarkey = mlngArkeyMalarkey
End Property


Public Property Let ArkeyMalarkey(ByVal lngNewValue As Long)

    If Ambient.UserMode = True Then
        ' It's runtime.
        MsgBox "You ninny! You can't set this at runtime!"
    Else
        mlngArkeyMalarkey = lngNewValue
    End If
        
End Property
  1. To test this code, close the code window, close the control designer, and switch to the EXE project test form. Create an instance of the ArkeyMalarkey control on the form.
  2. You should be able to see the control's ArkeyMalarkey property in the Properties window. Change it to some other value. You should be able to do this with no problem. This demonstrates that the property can be changed at design time.
  3. Now run the project by choosing the menu command Run, Start. Pause execution by clicking on the Pause button on the toolbar (or by pressing Ctrl+Break on your keyboard).
  4. If the Immediate window isn't already visible, press Ctrl+G to make it visible. In the Immediate window, enter the code
ArkeyMalarkey1.ArkeyMalarkey = 14
  1. When you press Enter, the message box should pop up, as illustrated in Figure 10.1. The property will be left unchanged. This property cannot be changed at runtime.

Figure 10.1 : What part of runtime don’t you understand?.

NOTE
When the user attempts to set a design-time-only property at runtime, your control should raise an error, rather than display a message box, as this simplified demonstration does. For more information on raising errors, see Chapter 15, "Debugging and Error Trapping."

Permitting Your Control to Act as a Container

Just as a Visual Basic form can contain one or more ActiveX controls, a control can also contain one or more ActiveX controls. Not all controls can act as containers for other controls; examples of controls that can contain other controls include the PictureBox control and the Frame control.

Controls are contained in other controls for a variety of reasons. For example, the Frame control gives end users a choice of options, usually presented in the form of multiple OptionButton controls.

Sometimes controls are contained in other controls for cosmetic purposes. A PictureBox grouping of a collection of controls enables you to place a different color or bitmap behind the control group. Additionally, when you move the PictureBox control, all the controls contained in it move along with it, which can be a real help when you're coding complicated Resize events.

To let your UserControl contain other controls, you set its ControlContainer property to True. Once you do that, users can deposit a hypothetically unlimited number of controls in it. The only problem with this, as the following demonstration shows, is writing code to handle them.

Demonstration

To see how to program a control that has the ability to host other controls, you'll create a control called ViewPort. This control is designed to conserve screen space by housing a large number of controls in a very small space on the screen. It can do this because not all of the controls are visible at once; the user must scroll left or right to locate the correct control within the ViewPort control. So, this control trades off ease of use for the end user with conservation of screen space.

The ViewPort control consists of nothing more than some code and a constituent horizontal scroll bar control. The scroll bar enables the user to scroll left and right to view all the controls.

To create this control:

  1. Start a new control project. Give the UserControl the name ViewPort.
  2. Add a horizontal scroll bar control (HScroll) to the UserControl. It doesn't matter where you place the scroll bar, since its dimensions and position on the control will be determined by the UserControl's Resize event. The control designer will look like Figure 10.2.
    Figure 10.2 : Visual design of ViewPort control.

  3. In the Properties window, set the UserControl's ControlContainer property to True.
  4. Set the HScrollBar's LargeChange property to 100, and its SmallChange property to 20.
  5. You'll need to write some code to make the scroll bar stretch to fill the width of the UserControl. The code looks like this:
Private Sub UserControl_Resize()
    HScroll1.Move 0, Height - HScroll1.Height, Width
End Sub
  1. Next, you need to create a new property for the control. This property will dictate the scrollable area of the control (as opposed to the visible width of the control, which is determined by the control's Width property). Call this property VirtualWidth. To implement it, enter the following code:
' Declaration
Private mlngVirtualWidth As Long
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
    PropBag.WriteProperty "VirtualWidth", mlngVirtualWidth, Width
End Sub

Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
    mlngVirtualWidth = PropBag.ReadProperty("VirtualWidth", Width)
End Sub

Public Property Get VirtualWidth() As Long
    VirtualWidth = mlngVirtualWidth
End Property

Public Property Let VirtualWidth(ByVal lngNewValue As Long)
    mlngVirtualWidth = lngNewValue
    HScroll1.Max = lngNewValue
End Property
  1. In the Declarations section of the code, declare an internal variable that stores the value of the horizontal scroll bar. You'll use this variable in the next step.
Private mlngPreviousScroll As Long
  1. Add the following code to the horizontal scroll bar's Change event. This code moves all of the controls contained in the control to the left or right whenever the scroll bar is changed. This will give the end user the impression that she is panning left or right when she clicks on the scroll bar, when in fact the controls are physically being moved each time she clicks.
Private Sub HScroll1_Change()
    Dim c As Control
    For Each c In UserControl.ContainedControls
        c.Left = c.Left + (mlngPreviousScroll - HScroll1.Value)
    Next
    mlngPreviousScroll = HScroll1.Value
End Sub

    This is the trickiest piece of code in the control. It uses the ContainedControls collection of the UserControl object to iterate through all the controls contained in the control, adjusting their Left properties so it appears as if the control is panning across them. In fact, though, the control isn't panning at all; the controls themselves are moved left or right.

  1. The control is ready to be sited on the form. Close the control designer and return to the test EXE project form.
  2. Create an instance of the ViewPort control on the EXE project form. Resize the control so it's big enough to accommodate three CommandButton controls.
  3. Create a new CommandButton on the form.
  4. With the CommandButton still selected, choose the menu command Edit, Cut (or use the keyboard shortcut Ctrl+X).
  5. Select the ViewPort control and select the menu command Edit, Paste (or use the keyboard shortcut Ctrl+V). This causes the command button to be contained by the ViewPort control.
  6. Repeat the process for two more command buttons, and adjust their position and Caption properties so they look like Figure 10.3.
    Figure 10.3 : ViewPort control with contained CommandButtons.

  7. Resize the ViewPort control so that only one of the three buttons is visible.
  8. Before you run the EXE project, you must add one line of code to the EXE project form. This code sets the VirtualWidth property as a function of the width of the control. Enter the following code in the Load event of the form:
Private Sub Form_Load()
    ' Set virtual width of view port to
    ' the right side of the rightmost command button
    ViewPort1.VirtualWidth = Command3.Left + Command3.Width
End Sub

  1. Run the EXE project by choosing the menu command Run, Start (or by using the function key F5). You should be able to use the scroll bar to scroll back and forth to see all of the buttons contained in the ViewPort control.

The ViewPort control should work for an unlimited number of contained controls, as long as the code you entered in the Load event of Form1 that sets the control's VirtualWidth property takes all the controls stored in the control into account; the property must always be large enough to store the rightmost control contained within.

NOTE
If you feel like getting a little more show-offy, you might consider adding a vertical scroll bar and some more code to this control to make it scroll in two dimensions, both horizontally and vertically. Such a control would be a very handy way to view large graphics in a small area on screen.

Implementing Access Keys

An access key is a key that the user presses in combination with the Alt key. Controls can be programmed to respond to access keys. A typical use for this feature is for a control to take the focus in response to an access key, but you could have a control respond by executing a method.

Controls that can respond to access keys have a Caption property that responds to the ampersand (&) character in a special way. When the user assigns a caption that contains an ampersand, it means that the next character in the caption string is to be treated as that control's access key. A control with a caption of ABC&DEFG would then have an access key of d (and the caption would display as ABCDEFG). So, to produce the result specified in this caption, the user would press ALT+D.

For example, let's say you have a form with a frame and two option buttons. The first option button has the caption &Jes, and the second button has the caption &Ne. (I'm talkin' Esperanto here; try to keep up with me.) Pressing Alt+J on your keyboard is the equivalent of clicking the Jes button, while pressing Alt+N is the equivalent of clicking Ne, as illustrated in Figure 10.4.

Figure 10.4 : Access keys allow an alternative to mouse clicks.

On the other hand, if you create a CommandButton control and give it the Caption property of J&erky, the control's access key becomes Alt+E. If you run the program and press Alt+J, the CommandButton's Click event is triggered.

Access Keys with Constituent Controls

If you rely on the behavior of constituent controls to provide access key behavior, you don't have to write any code at all. To see how this works, do the following:

  1. Set up a new control project with constituent Label and TextBox controls. Be sure to add the Label first, and then the Text Box.
  2. Give the UserControl the name TextLabel.
  3. Set the Text property of the constituent TextBox to nothing. The control should look like Figure 10.5.
    Figure 10.5 : TextLabel control.

  4. Insert the following code to delegate a Caption property to the Label control:
Property Get Caption() As String
    Caption = Label1.Caption
End Property

Property Let Caption(NewCaption As String)
    Label1.Caption = NewCaption
End Property

Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
    Caption = PropBag.ReadProperty("Caption")
End Sub

Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
    PropBag.WriteProperty "Caption", Label1.Caption
End Sub
  1. Close the form designer and switch to the EXE project form.
  2. Place a normal TextBox control on the form, then place an instance of your TextLabel control on the form.
  3. Set the Caption property of TextLabel1 to &Punkinhead. Make sure you include the ampersand, because that's the character that designates the access key. The form should look like Figure 10.6.
    Figure 10.6 : Test form with TextBox and TextLabel controls.

  4. Run the EXE project. You can see that the normal TextBox takes the focus, because its TabIndex property is zero. (This is the case because you added it to the form first.)
  5. Press the access key for the TextLabel control, Alt+P. You should be able to see that the focus changes-not to the Label, but to the TextLabel's constituent TextBox control.

Relying on a Label to serve as an access key for another control is a neat (albeit not-entirely-obvious) trick. It works because the Label can't take the focus itself, so instead, the next control in the tab order (in this case, the constituent text box) is given the focus instead.

Using the AccessKey Property and AccessKeyPress Event

Hijacking the Label control's access key works well when you have a Label in your control. But what happens when you want to (for example) build your own avant-garde command button out of a constituent PictureBox control? A PictureBox control doesn't respond to an access key, so you have to build that functionality into your control manually.

You can build manual access key functionality into your control by using the AccessKeys property and AccessKeyPress event of the UserControl. The AccessKeys property denotes the key or keys that will cause the AccessKeyPress event to be raised. The AccessKeyPress event enables you to write code that will take an action based on the end user pressing the control's access key. Whether the code in the AccessKeyPress event simply moves the focus onto your control or initiates an action (such as another event) is up to you. To see how this works:

  1. Create a new control project. Give the UserControl the name ClickyPicture.
  2. Add a constituent PictureBox control to the designer and make it roughly the same height and width as the designer.
  3. Set Picture1's AutoRedraw property to True.
  4. Create a Click event for the control by adding the following code:
' Declarations
Public Event Click()

Private Sub Picture1_Click()
    RaiseEvent Click
End Sub
  1. Give your PictureBox a textual component in its Paint event by entering the following code:
Private Sub UserControl_Paint()
    Picture1.Font.Bold = True
    Picture1.Font.Size = 24
    Picture1.Font.Underline = True
    Picture1.Print "C";
    Picture1.Font.Underline = False
    Picture1.Print "lick me!"
    Picture1.CurrentX = 0
    Picture1.CurrentY = 0
End Sub
  1. In order to show that the control has responded to your pressing of the access key, enter the following code in the UserControl's AccessKeyPress event:
Private Sub UserControl_AccessKeyPress(KeyAscii As Integer)
    SetFocus
    Debug.Print "You sunk my battleship."
End Sub
  1. Finally, in the Properties window, change the UserControl's AccessKeys property to the letter c, as illustrated in Figure 10.7.

Figure 10.7 : AccessKeys property.

This will cause the control's AccessKeyPress event to be triggered when the end-user presses the keystroke combination Alt+C.

To test the control, place an instance of it on an EXE project form, along with a normal control such as a text box. The EXE project form should look like Figure 10.8. Then run the EXE project by clicking in the normal text box, then pressing Alt+C. You should be able to see the focus move off of the text box. The Immediate window will also indicate that the control's AccessKeyPress event was run.

Figure 10.8 : ClickyPicture control.

So you can see in this example how to provide an access key through the AccessKeyPress event. Providing such shortcuts can make your control easier to use, providing users with the kind of interface they expect from controls.

Controls That Are Invisible at Runtime

Not too many controls are invisible at runtime. This is because the vast majority of controls provide user-interface elements, and as every user-interface designer knows, an invisible user interface is an unhappy user interface.

Controls that are invisible at runtime usually provide some sort of calculation, or they're used as wrappers to Windows API calls. The Timer control is the most common example of this. Because it only serves to tick off time, the Timer doesn't need to have a visual interface; instead, it serves only to generate events after a certain period of time has passed.

Creating a control that is invisible at runtime is simplicity itself-simply set the control's InvisibleAtRuntime property to True.

NOTE
Controls that are invisible at runtime do not generate Paint events. This seems like a no-brainer (because nothing that's invisible ever needs to be painted) but it's worth mentioning. In Chapter 11 you'll see an example of a control that is desperately crying out for a Paint event, but simply can't have one because it's invisible at runtime.

Demonstration

To set up a control that is invisible at runtime:

  1. Create a new control project. Give the UserControl the name AnnoyingPing.
  2. Add a Timer control to the UserControl.
  3. Set the UserControl's InvisibleAtRuntime property to True.
  4. Set the Timer's Interval property to 5000. Because the Interval property is measured in milliseconds, this will cause the Timer to raise its Timer event every five seconds.
  5. Double-click on the Timer control to get to its code window. In the code window, write the following code:
Private Sub Timer1_Timer()
    If Ambient.UserMode = True Then
        Debug.Print "This has been an annoying ping."
        Beep
    End If
End Sub

(You're beginning to see why we call this control AnnoyingPing. You're also beginning to see why I live in a two-bedroom apartment all by myself.)

One interesting thing about the AnnoyingPing control is the fact that it only beeps when the program it's in is running (that is, Ambient.UserMode) is True. This conditional test is necessary because the Timer control will happily continue to tick off Timer events no matter whatÐwhether it's runtime or design-time. If only my dad's old Ford Pinto were that reliable.

Anyway, to test this control, close the form designer, create an instance of the control on an EXE project form, then run the EXE project. The AnnoyingPing control will start pinging wildly. You may wish to consider dropping this program off on the doorstep of any cranky old men (or ladies) you have living in your neighborhood. Try as they might, they won't be able to figure out what's causing the AnnoyingPing control to annoy them, because it has no visual interface. Science marches on.

NOTE
It's a good idea to consider placing the functionality of an invisible control in some other Visual Basic component, such as a class or an ActiveX DLL (formerly referred to as an OLE DLL). This is because an ActiveX control can have greater overhead (in terms of distributables and potentially also in terms of run-time resources) than a code module or a DLL. You can't create DLLs with the Control Creation Edition, but you can create them with the Professional or Enterprise edition of Visual Basic.

Controls That Align to the Edges of Forms

Some controls, such as the PictureBox control, have the ability to automatically align themselves to the edge of their containers. Such controls can be set to align themselves to either the top, left, right, or bottom of the form on which they reside. Such controls also automatically resize themselves when their parent form is resized.

Controls that are alignable expose an Align property. The Align property can be set to None (meaning the control's alignment feature is disabled), Top, Bottom, Left, or Right.

Alignability is used most often to implement a status bar that sits at the bottom of the form. You can make a control align to the edge of its container by setting the Alignable property of the UserControl to True.

Since status bars usually have textual and graphical components to inform the user of what's going on in the application, it would make sense to create a status bar control out of a PictureBox control. But because a PictureBox control doesn't have a Caption property, you'll have to create one yourself.

Demonstration

To demonstrate how to implement the UserControl's Alignable property, you'll create a status bar control that can align itself to the edge of the form it resides on and can resize automatically to match the form. To do this:

  1. Create a new Visual Basic control project.
  2. Add a constituent PictureBox control to the UserControl. Set the PictureBox's Height property to about 375 twips. (It does not matter how wide the control is, because its width will adjust automatically to the width of its parent form.)
  3. Give the UserControl the name StatusBar.
  4. Set the UserControl's Alignable property to True.
  5. To enable the control's Caption property and drawing events, enter the following code:
Private mstrCaption As String

Private Sub UserControl_InitProperties()
    mstrCaption = Extender.Name
End Sub

Private Sub UserControl_Paint()
    Picture1.Cls
    Picture1.Font.Name = "MS Sans Serif"
    Picture1.Font.Size = 10
    Picture1.CurrentX = 0
    Picture1.CurrentY = 0
    Picture1.Print mstrCaption
End Sub

Private Sub UserControl_Resize()
    Picture1.Move 0, 0, ScaleWidth, ScaleHeight
    UserControl_Paint
End Sub

Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
    mstrCaption = PropBag.ReadProperty("Caption", Extender.Name)
End Sub

Public Property Get Caption() As String
    Caption = mstrCaption
End Property

Public Property Let Caption(ByVal strNewValue As String)
    mstrCaption = strNewValue
    UserControl_Paint
End Property

Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
    PropBag.WriteProperty "Caption", mstrCaption, Extender.Name
End Sub
  1. Close the control designer and switch to the text EXE project form.
  2. Place an instance of the StatusBar control on the form.
  3. Set StatusBar1's Align property to 2-vbAlignBottom.
  4. Notice how the control snaps to align itself with the bottom of the form.
  5. Set StatusBar1's Caption property to the text "Spoon." The form should look like Figure 10.9.

Figure 10.9 : Form with StatusBar control.

Adding an About Box

An About box is a way to stamp your control with your name or your company's name, as well as other information about your control. Including this information has become more important now that ActiveX controls are downloadable over the Internet. It's possible that users will download your control and scarcely pay attention to where it came from. Which means that when they want to pay you big bucks for the licensed version of your control (or harass your tech support people because the control doesn't work right) they have no idea whom to call. Providing an About box resolves that dilemma by including a sort of electronic business card with every control you create.

In addition to information about your control and your company, you might also consider adding information on your online presence in an About box, including your company's Web page, if you have one. You can add anything to the About box you can add to a normal Visual Basic form.

The standard Visual Basic way to provide an About box in a control is to attach the About box to the control's About property. This "property" can't be set, so it really acts like a method, but it's customarily implemented as a property so that users of your control can access it easily through the About property in the Properties window. The About property in the Properties window is illustrated in Figure 10.10.

Figure 10.10 : AboutBox property in the Properties window.

NOTE
It's common for licensed, commercial controls to display their About boxes every time the control is run on a system that does not have a license to run the control. This enables unlicensed users to play with the control and see how it works, even though they haven't paid for it yet. But it discourages them from redistributing the control in the applications they build, because, you know, who wants to see your goofy About box every time the control runs? For more information on licensing your control, see Chapter 12, "Distributing Your Control."

To create your own About box, do the following:

  1. In the Project Explorer, right-click on Project1.
  2. Project1's Context menu appears. Click on Add.
  3. The Context menu's submenu appears. Click on Form.
  4. The Add Form dialog pops up. Click on Form, if it is not selected already, then click on Open.
  5. A new form is added to Project1. Change the form's properties as shown in Table 10.1.
  6. Add whatever other visual elements you want to the About box. (In my About box, for example, I chose to include a graphic promoting the virtues of cheese.)
  7. When you're done designing the About box, close it and return to your control designer.

Table 10.1 Form properties

PropertySetting
NamefrmAbout
BorderStyle3 - Fixed Dialog
CaptionAbout
StartUpPosition2 - CenterScreen

If you don't care to create your own About box for this demonstration, you can instead add the About box About.frm (from the CD-ROM that accompanies this book) to your template project. To add this About box to your project:

  1. Right-click on Project1 in the Project Explorer. Project1's context menu pops up. Select Add. Then, from the submenu, choose Add File (as pictured in Figure 10.11).
    Figure 10.11 : Project context menu.

  2. The Add File dialog appears. Locate the file About.frm on the CD-ROM, select it, and click on Open.
  3. The file is added to Project1.

NOTE
Be sure you've added the About box to Project1, your control project, not Project2, your test EXE project.

Now that your project contains an About box, you need to enable it. To do this:

  1. In your UserControl, write the following code:
Public Sub AboutBox()
    frmAbout.Show vbModal
    Unload frmAbout
    Set frmAbout = Nothing
End Sub

NOTE
The code Set frmAbout = Nothing is used to remove the form from memory.

  1. Select the menu command Tools, Procedure Attributes. The Procedure Attributes dialog box appears.
  2. Choose AboutBox from the Name combo box, if it isn't already selected.
  3. Click on Advanced. The Procedure Attributes dialog box expands.
  4. In the Procedure ID combo box, select AboutBox. This designates your AboutBox subroutine as the official AboutBox procedure for this control. As you'll see later, Visual Basic treats the AboutBox procedure in a special way.
  5. Click on OK.
  6. Close the code window, close the control designer, and place an instance of the control on Form1.
  7. In the Properties window, you should be about to see an About property at the top of the list. Click on the property, then click on its ellipsis button.
  8. The About box is displayed, as illustrated in Figure 10.12.

Figure 10.12 : About box.

Project Templates

If you find yourself creating the same type of project again and again, you should consider setting up a project template. Project templates are a new feature of Visual Basic that let you create a prefabricated set of visual design and code elements that are included in every project you create.

It's worth noting that the CtlGroup project you've been basing most of your Visual Basic control projects on is itself a template. You can modify that template as well. For example, if your control projects always contain a certain set of API declarations, ActiveX controls (built by you or somebody else), a custom class library you've devised, or a third-party add-in, you can alter the standard project templates to load these automatically every time you create a new project.

NOTE
Project templates take the place of the autoload.vpj file in Visual Basic 4.0 that loaded a programmer-defined set of custom controls each time you launched VB.

Demonstration

Let's say you work for a software company that makes ActiveX controls that enable users to create custom charts and other graphics. Your company's standards dictate that every control have a similar About box with your company's logo. Additionally, since all your company's controls have to do with charting, you want all of your new controls to contain a PictureBox control with the appropriate resize code already included.

Start by creating the visual design of the project that will become your template. To do this:

  1. Create a new control project based on the template CtlGroup. Give the UserControl the name BasicPicture.
  2. Add a PictureBox control to the control designer.
  3. Double-click on the UserControl to open its code window. In the UserControl's Resize event, type the following code:
Private Sub UserControl_Resize()

' The Move method is the fastest and most
' sanitary way to resize a control
    Picture1.Move 0, 0, ScaleWidth, ScaleHeight

End Sub
  1. Close the code window, then close the BasicPicture control's form designer.

At this point, you could have added more code to expose the constituent Picture property of Picture1 and take care of PropertyBag operations (for example), but we've proven our point for the time being.

Now it's time to take the skeletal project group you've created and move it into the Visual Basic templates directory so you can use it as a template. The trick here is that you're going to save the control group (that is, the .VBG file) into the templates folder, but you're going to save the individual elements of the project into a separate folder alongside the templates folder. That way, only the project group becomes a template. To do this:

  1. Select the menu command File, Save Project Group.
  2. Visual Basic will offer to save the control file, BasicPicture.ctl, first.
  3. Use the file dialog box to switch to the \vb\template folder.
  4. Click on Create New Folder, to create a new folder alongside the template folder. Call this new folder BasicPicture. The CreateNewFolder button is illustrated in Figure 10.13.
    Figure 10.13: The CreateNewFolder button.

  5. Double-click on the new BasicPicture folder, then click on Save to save the file.
  6. Next, Visual Basic will offer to save the control project file. Save it as BasicPicture.vbp in the BasicPicture folder.
  7. Visual Basic will then offer to save the EXE project test form. Name this file BPTest.frm and save it in the BasicPicture folder.
  8. The next file to save is the EXE project file. Call this BPProject.vbp and save it in the BasicPicture folder.
  9. Finally, you are ready to save the project group. But don't save it into the BasicPicture folder. Instead, back up one folder level by clicking on Up One Level, as illustrated in Figure 10.14.
    Figure 10.14 : The Up One Level button.

  10. Next, double-click on the Projects folder. This is where Visual Basic saves project templates. Give your project group the name Picture.vbg, then click on Save.
  11. The project is saved. Just in case you lost track of what just happened, Table 10.2 contains a handy summary of the filenames and where they should be stored.

Table 10.2 Filenames for template project

Project Group\vb\template\projects\Picture.vbg
Project\vb\template\BasicPicture\BasicPicture.vbp
BasicPicture control\vb\template\BasicPicture\BasicPicture.ctl
Test project\vb\template\BasicPicture\BPTest.vbp
Test form\vb\template\BasicPicture\BPTest.frm

To see that your project has been made into a template, start a new project by choosing the menu command File, New Project. The New Project dialog box appears. You should be able to see your BasicPicture template in the dialog box, as illustrated in Figure 10.15.

Figure 10.15 : New Project dialog box .

Changing the Default Templates Folder

If for some reason you don't care to store your templates under the VB folder, you can change it to whichever directory you like. For example, if you are part of a team of developers, you might want to store a collection of templates on a file server accessible to everyone in your work group. In this case, you'd want all the developers in your work group to use the same templates folder.

To do this, choose the Tools, Options menu command, then select the Environment tab. The dialog box looks like Figure 10.16.

Figure 10.16 : The Environment tab under Tools, Options.

Enter your preferred directory in the Templates Directory area, then click OK. The directory you chose will be the directory Visual Basic will look in to find templates. If you're working in a group, each member of the group must set this option in their copy of Visual Basic.

Adding a Toolbox Bitmap

You can add a bitmap to your UserControl. This bitmap represents your control in the Visual Basic toolbox.

Adding a bitmap that will represent your control in the Visual Basic toolbox is similar to setting the Picture property of a PictureBox control.

You add a toolbox bitmap by assigning a bitmap to the ToolboxBitmap property of your UserControl. The bitmap you assign can be of any size. However, you should tailor your toolbox bitmaps to the standard size of 16 pixels wide by 15 pixels high. If you make your bitmaps any bigger than that, Visual Basic will automatically scale them down and they'll look heinous.

Demonstration

To add a bitmap to a control project:

  1. Open or create a new control project.
  2. Open the control designer.
  3. In the control designer's Properties sheet, click on the ToolboxBitmap property, then click on its ellipsis button.
  4. The Load Bitmap dialog box appears. Locate and select the file tombo.bmp, which is on the CD-ROM that accompanies this book. When you've located it, click on Open.
  5. Close the form designer.
  6. Open Form1. You should be able to see that the nice new bitmap appears in the toolbox (as in Figure 10.17) and the stinky old generic toolbox bitmap is nowhere to be seen.

Figure 10.17 : Icon in the toolbox.

NOTE
Paint Shop Pro is a righteous tool for creating goofy little bitmaps like this. It's orders of magnitude better than Windows Paintbrush. Better yet, it's shareware, so you can torture it before you purchase it. You can get a copy from the good folks as JASC, Inc. by visiting their web site at http://www.jasc.com.

Summary

In this chapter, you learned probably more than any human being should be permitted to learn about a smattering of varied yet vital features of a well-crafted ActiveX control. In the next chapter, you'll learn how to take advantage of the Windows API to enable your control to perform tasks not available in Visual Basic.