Exposing Events of Constituent Controls

See Also

The mechanism for exposing events is different from the delegation used to expose properties and methods. You expose an event in a constituent control by raising your own event, as in the following code fragment from a UserControl code module:

' Declaration of your control's Click event.
Public Event Click()

' When the txtBase text box control raises a Click,
'   your control forwards it by raising the Click
'   event you declared.
Private Sub txtBase_Click()
   RaiseEvent Click
End Sub

' You may also want to raise your control's Click event
'   when a user clicks on the UserControl object.
Private Sub UserControl_Click()
   RaiseEvent Click
End Sub

Notice that your Click event may be raised from multiple locations in your code. You can add your own code before and after raising the Click event.

The Difference Between Events and Properties or Methods

It may help to think of properties and methods as incoming, and events as outgoing. That is, methods are invoked from outside your control, by the developer who's using your control. Thus, the developer invokes a method of your UserControl object, and you respond by delegating to the method of your constituent control.

By contrast, events originate in your control and are propagated outward to the developer, so that she can execute code in her event procedures. Thus, your UserControl object responds to a click event from one of its constituent controls by raising its own Click event, thus forwarding the event outward to the developer.

Mouse Events and Translating Coordinates

The MouseDown, MouseMove, and MouseUp event procedures for the UserControl object have arguments giving the event location in the ScaleMode of the UserControl. Before raising your control's MouseDown, MouseMove, and MouseUp events, you must translate the event location to the coordinates of the container.

How do you know what the container's ScaleMode is? You don't. You don't even know if the container has a ScaleMode property. Fortunately, you don't have to worry about that, because Visual Basic provides the ScaleX and ScaleY methods for translating coordinates.

The following code fragment shows how to expose the MouseMove event from a UserControl whose ScaleMode is Twips. It assumes that the MouseMove event has been declared with the correct parameters (you can use the ActiveX Control Interface Wizard to generate event declarations).

Private Sub UserControl_MouseMove(Button As Integer, _
      Shift As Integer, X As Single, Y As Single)    RaiseEvent MouseMove(Button, Shift, _
      ScaleX(X, vbTwips, vbContainerPosition), _
      ScaleY(Y, vbTwips, vbContainerPosition))
End Sub

The ScaleX method translates from vbTwips to whatever the coordinate system of the container is; this information is obtained by Visual Basic using low-level ActiveX calls. The return value from ScaleX (the new X coordinate) is passed as the X argument of the MouseMove event.

If you change the ScaleMode of your UserControl at run time, you should use UserControl.ScaleMode instead of vbTwips in the code above, so that the correct translation is done regardless of current ScaleMode setting.

Importance of Testing

The code above, with its explicit vbTwips, will have to be changed if the ScaleMode of the UserControl is permanently changed at design time. It's important to test your control's response to mouse moves on forms with a variety of ScaleMode settings, to ensure that ScaleX and ScaleY have the correct arguments.

Constituent Controls

If your control includes constituent controls that have mouse events, you'll have to raise your control's mouse events there, too. Otherwise there will appear to be dead spots on your control, where the mouse events don't occur.

Constituent control mouse events are slightly more complicated, because they provide X and Y in the coordinates of the constituent control rather than the UserControl. The following code fragment shows how this is handled when the constituent control has the same ScaleMode as the UserControl:

Private Sub Label1_MouseMove(Button As Integer, _
      Shift As Integer, X As Single, Y As Single)    RaiseEvent MouseMove(Button, Shift, _
      ScaleX(X + Label1.Left, _
         vbTwips, vbContainerPosition), _
      ScaleY(Y + Label1.Top, _
         vbTwips, vbContainerPosition))
End Sub

Adding the Left and Top properties of Label1 to the X and Y coordinates translates them to UserControl coordinates; ScaleX and ScaleY then transform the results to container coordinates.

If the UserControl's ScaleMode were set to Pixels, you would first have to call ScaleX and ScaleY to translate X and Y to pixels, before adding the results to Label1.Top and Label1.Left. The results of the addition would then be passed to ScaleX and ScaleY, as above.

Other Events that Provide Position

If you create events of your own that pass location information to the container, you must use the same technique to transform the locations into the container's coordinates. If you want to pass width and height information, use vbContainerSize instead of vbContainerPosition when calling ScaleX and ScaleY.

Using the ActiveX Control Interface Wizard

The ActiveX Control Interface Wizard can greatly simplify the task of forwarding events. This is discussed in the related topic "Events Your Control Should Raise."