Dear John, How Do I... Create an ActiveX Control?

The ActiveX Control Interface Wizard lets you base new controls on existing controls. The code generated by the wizard can be difficult to understand, however, so this section shows you how to create an ActiveX control manually. Once you understand the parts of an ActiveX control, it's easier to use the ActiveX Control Interface Wizard.

ActiveX Control Design Steps

To create an ActiveX control, follow these steps:

Create a new ActiveX control project.

  1. In the UserControl window, draw the visual interface of the control using graphics methods and controls from the Toolbox, just as you would for a form.

  2. In the code window, add an event procedure to resize the visible aspects of the control when the user resizes the control on a form.

  3. Add the properties, methods, and events that your control will expose to applications using it. These elements define the interface of your control.

  4. Write code that implements your control's functionality. This code determines the behavior of your control.

  5. Add a new Standard EXE project within Visual Basic to provide a way to debug the control.

  6. From the File menu, choose Make to compile the control into an OCX file.

The following text discusses the first six steps in greater detail while providing instructions on how to build a sample control named Blinker (BLINKER.VBP). The Blinker control is used to flash controls or windows on screen for emphasis. Debugging and compiling the control are discussed in the two sections that follow, "Dear John, How Do I... Debug a Control?" and "Dear John, How Do I... Compile and Register a Control?"

Creating the ActiveX Control Project

To create an ActiveX Control project, follow these steps:

  1. From the File menu, choose New Project to display the New Project dialog box.

  2. Double-click the ActiveX Control icon. Visual Basic creates a new project and displays the UserControl window.

  3. From the Project menu, choose Project Properties to display the Project Properties dialog box.

  4. In the Project Name text box, type VB6WkSamp. In the Project Description text box, type VB6 Workshop Blinker Sample Control and then click OK. The project name is used to name the OCX file; the project description is displayed in the Components dialog box.

  5. In the UserControl Properties window, set the Name property to Blinker. This is the class name for the control. It will be used when naming instances of the control as they are drawn on a form: Blinker1, Blinker2, and so on.

  6. Choose Save Project from the File menu, and save the Blinker control as BLINKER.CTL and the project as BLINKER.VBP.

Drawing the Interface

The Blinker control flashes other controls or windows in your application in order to draw the user's attention. This isn't spectacularly hard to do—which is one reason it makes a good sample. The Blinker control uses an ActiveX UpDown control, an intrinsic TextBox control, and an intrinsic Timer control, as shown in Figure 6-1.

Click to view at full size.

Figure 6-1. The visual interface of the Blinker control, which contains one ActiveX control and two intrinsic controls.

To create the visual interface of the Blinker control, follow these steps:

  1. From the Project menu, choose Components to display the Components dialog box.

  2. On the Controls tab, check the Microsoft Windows Common Controls-2 6.0 (MSCOMCT2.OCX) checkbox and click OK. Visual Basic adds the UpDown control and other common Windows controls to the Toolbox.

  3. In the UserControl window, draw a text box. In the Properties window, set its Name property to txtRate and its Text property to 0.

  4. Draw an UpDown control next to the text box, and set its Name property to updnRate.

  5. Draw a Timer control, and set its Name property to tmrBlink.

Resizing the Control

You can be sloppy when drawing the Blinker interface because the size and position of the visible controls must be handled in code anyway. When users draw the Blinker control on a form, they can click and drag the control to make it the size they want. Resizing triggers the UserControl_Resize event procedure, which positions and sizes the visible interface of the control appropriately.

The following code handles resizing the Blinker control:

`Code for control's visual interface
Private Sub UserControl_Resize()
    `Be sure visible controls are
    `positioned correctly within the
    `UserControl window
    updnRate.Top = 0
    txtRate.Top = 0
    txtRate.Left = 0
    `Resize visible controls
    `when user control is resized
    updnRate.Height = Height
    txtRate.Height = Height
    `Adjust UpDown control's width up
    `to a maximum of 240 twips
    If Width > 480 Then
        updnRate.Width = 240
    Else
        updnRate.Width = Width \ 2
    End If
    `Set width of text box
    txtRate.Width = Width - updnRate.Width
    `Move UpDown control to right edge of
    `text box
    updnRate.Left = txtRate.Width
End Sub

As you can see, I got a little fancy with the resize code for the UpDown control. Rather than fix it at 240 twips, I let it scale down if the user makes the Blinker control less than 480 twips. Your resize code can be as simple or as complicated as you want.

Adding Properties, Methods, and Events

In addition to the standard size, position, and visibility properties provided with all controls, the Blinker control has a TargetObject property, which specifies the object that will blink, and an Interval property, which specifies how many times per second the object should blink. The Blinker control also includes a Blinked event, which occurs after the object blinks, and a Blink method, which sets the TargetObject and Interval properties.

ActiveX control properties, methods, and events are defined in the same way as class properties, methods, and events. The following code shows the TargetObject, Interval, Blinked, and Blink members of the control:

Option Explicit

`Windows API to flash a window
Private Declare Function FlashWindow _
Lib "user32" ( _
    ByVal hwnd As Long, _
    ByVal bInvert As Long _
) As Long

`Blinked event definition
Public Event Blinked()

`Internal variables
Private mobjTarget As Object
Private mlngForeground As Long
Private mlngBackground As Long
Private mblnInitialized As Boolean

`Public error constants
Public Enum BlinkerErrors
    blkCantBlink = 4001
    blkObjNotFound = 4002
End Enum

`Code for control's properties and methods
`~~~.TargetObject
Public Property Set TargetObject(Setting As Object)
    If TypeName(Setting) = "Nothing" Then Exit Property
    `Set internal object variable
    Set mobjTarget = Setting
End Property

Public Property Get TargetObject() As Object
    Set TargetObject = mobjTarget
End Property

`~~~.Interval
Public Property Let Interval(Setting As Integer)
    `Set UpDown control--updates TextBox and
    `Timer controls as well
    updnRate.Value = Setting
End Property

Public Property Get Interval() As Integer
    Interval = updnRate.Value
End Property

`~~~.Blink
Sub Blink(TargetObject As Object, Interval As Integer)
    `Delegate to TargetObject and Interval properties
    Set Me.TargetObject = TargetObject
    Me.Interval = Interval
End Sub

The TargetObject Property Set procedure sets the target object that should blink by assigning an internal object variable. The Interval property merely sets or returns the value of the UpDown control—this changes the value in the text box and thereby changes the Timer control's Interval property. The Blinked event is defined here but is triggered from the Timer event. Next I'll show you the event procedures for the UpDown, Timer, and TextBox controls—where the real work is done.

Programming the Control's Behavior

So far, the Blinker control looks nice, but doesn't do anything. The control's behavior—flashing a control or window—is determined by code in the UpDown control's Change event procedure, the timer's Timer event procedure, and the text box's Change event procedure, as shown here:

`Code for control's behavior
Private Sub updnRate_Change()
    `Update the text box control
    txtRate.Text = updnRate.Value
End Sub

Private Sub tmrBlink_Timer()
    On Error GoTo errTimer
    `Counter to alternate blink
    Static blnOdd As Boolean
    blnOdd = Not blnOdd
    `If the object is a form, use FlashWindow API
    If TypeOf mobjTarget Is Form Then
        FlashWindow mobjTarget.hwnd, CLng(blnOdd)
    `If it's a control, swap the colors
    ElseIf TypeOf mobjTarget Is Control Then
        If Not mblnInitialized Then
            mlngForeground = mobjTarget.ForeColor
            mlngBackground = mobjTarget.BackColor
            mblnInitialized = True
        End If
        If blnOdd Then
            mobjTarget.ForeColor = mlngBackground
            mobjTarget.BackColor = mlngForeground
        Else
            mobjTarget.ForeColor = mlngForeground
            mobjTarget.BackColor = mlngBackground
        End If
    Else
        Set mobjTarget = Nothing
        GoTo errTimer
    End If
    `Trigger the Blinked event
    RaiseEvent Blinked
    Exit Sub
errTimer:
    If TypeName(mobjTarget) = "Nothing" Then
        Err.Raise blkObjNotFound, "Blinker control", _
            "Target object is not valid for use with this control."

    Else
        Err.Raise blkCantBlink, "Blinker control", _
            "Object can't blink."
    End If
End Sub

Private Sub txtRate_Change()
    `Set Timer control's Interval property
    `to match value in text box
    If txtRate = 0 Then
        tmrBlink.Interval = 0
        tmrBlink.Enabled = False
        mblnInitialized = False
        `If blinking is turned off, be sure object 
        `is returned to its original state
        If TypeOf mobjTarget Is Form Then
            FlashWindow mobjTarget.hwnd, CLng(False)
        ElseIf TypeOf mobjTarget Is Control Then
            mobjTarget.ForeColor = mlngForeground
            mobjTarget.BackColor = mlngBackground
        End If
    Else
        tmrBlink.Enabled = True
        tmrBlink.Interval = 1000 \ txtRate
    End If
End Sub

The UpDown control's Change event procedure simply copies the value of the UpDown control to the text box. The timer's Timer event procedure handles the flashing by using the FlashWindow API function for forms or by swapping the ForeColor and BackColor properties for visible controls. Using the FlashWindow API function is more effective than swapping the ForeColor and BackColor properties. The text box's Change event procedure handles the blinking rate.

SEE ALSO