Visual Basic OLE DLL to create a User-Tool and a User-Interface


The function to create text entities containing single unit fractions text will be called VBSUFText (for Visual Basic Single Unit Fraction Text).

Using the same Visual Basic project created in Tutorial 1, DLL to Show, Hide, or Toggle Point Display, add the VCTOOL32.BAS declaration module to the project. A couple of the Visual CADD API routines used by this example are in the VCTOOL32.DLL and are declared in the VCTOOL32.BAS module. This latter module must be added to our project in order for Visual Basic to have the proper declarations for these routines. Use the File, Add File menu command to add the VCTOOL32.BAS file, which should be in the VCDeclares subdirectory of your project directory tree.

To start the VBSUFText function, add the following function declaration to GENERAL.CLS:

  Public Function VBSUFText(dummy As String)

Recall that OLE DLLs called from Visual CADD need to accept a string parameter. No parameter is used by VBSUFText, so we must specify a dummy.

The dialog box created by this example will be a form named SUFText (for Single Unit Fraction Text). The only thing our function needs to do is load this form.

	Load SUFText
  End Function

All the real work will be done by the form. A new form is added to your Visual Basic project by using Insert, Form in the menu. The form (or dialog box) should be created with four controls: (1) a TextBox for the numerator, (2) a TextBox for the denominator, (3) a CommandButton for &OK, and (4) a CommandButton for &Cancel.

The form should have a name SUFText and the caption will be "Single Unit Fraction". When you save the form, save it with the file name SUFractionText.FRM in your project folder.

While you are editing the properties of the form, a few other changes are required. The KeyPreview property of the form must be set to True. This property allows the form to process KeyPress, KeyDown, and KeyUp events before the controls in the form. This preview of keys is necessary for a Visual CADD user-tool in order for the form to process cancel (or abort) events triggered by Visual CADD.

The Visible property of the form should be set to False. The form will be hidden from view until after the user has been prompted for, and placed, the location of the text.

Some of the controls on the form must be modified to accommodate the workaround related to using Visual Basic dialogs in an OLE DLL with Visual CADD. For each control which is a tab stop, enter any character, for example "x", into the Tag property. (A tab stop is a control which the user can reach by using the tab key to move from control to control within the dialog.) All other controls should have nothing in the Tag property. The Tag property is an extra property provided by Visual Basic for the program's use - Visual Basic does not use it for any other purpose. The Tag property will later be checked to identify which controls have tab stops set. (More on this later.)

The programmer decides which controls to make tab stops, but tab stops usually include the OK and Cancel command buttons and the text boxes in a dialog. In deciding which controls should be made tab stops, the programmer should consider how the user would ordinarily enter data into the dialog and the easiest way for the user to do that. In this example, we suggest that both text boxes and both command buttons be tab stops. So, you should add an "x" to the Tag property of each of these four controls.

If the user of your application fails to enter anything into the text boxes for the numerator or denominator, we should have a default fraction which will appear in the single unit fraction. To set a default of ½, for example, the numerator Text property may be set to a default of "1" and the denominator Text property may be set to a default "2".

The following code assumes that the names of the controls are Numerator, Denominator, BtnOK, and BtnCancel, which are set with the Name property. These names can be changed, but remember to change the affected code, also.

The BtnOK button is set as the default button by setting the Default property to True and the BtnCancel button is set as the cancel button by setting the Cancel property to True.

For help with creating and editing the form and its controls, refer to your Visual Basic documentation.

The tool works by placing text at a user-selected point in the drawing. So, the program will need a Point2D to hold the location of the text. Add the following to the general declarations section of the code for the SUFText form:

   '       mousedown point for text location
   Dim pt0 As Point2D

   '       Visual CADD error code
   Dim iError As Integer

Whenever a form loads, Windows automatically generates an event called Load to tell your program the form was loaded. Load events for forms are processed by a subroutine, or method, called Form_Load. Because the Load event occurs when the form is loaded, the Form_Load method is a good place to perform any initialization required by the form.

In this example, the form will be used to create a user-tool and to process the Visual CADD events. Processing Visual CADD events requires that the program tell Visual CADD to share Visual CADD events with it. The Visual CADD API routine VCSetAlertApp does this and requires two parameters besides the error code.

First, Visual CADD must know to which form to send events. VCSetAlertApp tells Visual CADD the form by supplying a handle to the form's window, which is the hWnd property of the form. Note that this handle is a window handle, defined by Windows, and is different from both a placement handle and an entity handle in Visual CADD or the Visual CADD API.

Second, Visual CADD must know which events your program wishes to share. There are many (see the API help file), but this example only needs two. One is a mouse-down event, which corresponds to a left-button click when placing an entity on a Visual CADD drawing. Visual CADD users know that points may be placed in a number of ways, including mouse-clicks, keyboard entry, tracking, and snaps. Whenever Visual CADD is placing a point, it treats all of these alternatives as the equivalent of a mouse-click. For example, if a point is being placed using tracking, the Pen Up (PU) which finally places the point is treated as though the user clicked the mouse at that point. Thus, the program needs only share the mouse-down event to share all point placement.

The second event needed by most user-tools is an abort event. When the Visual CADD user is placing a point, the ESCAPE key will abort the placement. Because this example will prompt the user to place a point, it should also share the abort event so it knows if the user aborted the user-tool.

The VCTYPE32.BAS module defines a number of codes for specifying events. When your program needs several events, their codes should be connected with the Or operator, which causes a 'bitwise or' to be performed on the numeric codes.

The two codes needed are ALERT_APP_UTOOL_MOUSEDOWN Or ALERT_APP_UTOOL_ABORT, for mouse-down and abort.

In the code section of the SUFText form, click "Form" under "Object", and "Load" under "Proc" to take you to the Sub Form_Load screen. Enter the following:

  Private Sub Form_Load()

	'       capture mousedown and abort messages
        Call VCSetAlertApp(iError, SUFText.hWnd, _
         	ALERT_APP_UTOOL_MOUSEDOWN Or _
         	ALERT_APP_UTOOL_ABORT)

As explained in Example of Parsing the Database and Modifying Entities, tools should support undo by using VCBeginOperation. So, add the following:

	'       begin for undo
                Call VCBeginOperation(iError)

Visual CADD is told that a user-tool is being created by using VCSetUserTool, which takes three parameters. These are the number of steps, the native command defined by the user-tool, and the first user prompt. This example only requires one point placement, so there will be only one step. The native command to be defined in the CMDEXT.DEF will be SUFText and the prompt will be "Pick Starting Point" (the same as Text Editor (TE)).

And, that is all that is required for initialization and we can end the subroutine. So, add the following:

	'       user tool prompt
        Call VCSetUserTool(1, "SUFText", "Pick Starting Point")
  End Sub

When a form is unloaded, an Unload event is automatically created. Because the SUFText is creating a user-tool, the form will be unloaded when the user-tool is done. So, the Form_Unload method is a good place to put code for clean-up.

The only thing needed in our form is to tell Visual CADD not to send any more events, because the user-tool is done. This is done with VCClearAlertApp with the same window handle used for the VCSetAlertApp.

Now, we put VCBeginOperation in the Form_Load method, so it may seem natural to put the VCEndOperation in the Form_Unload method. However, because we do not know if the form is unloading due to a cancel (or abort) or due to successful completion, we will need to put the VCEndOperation elsewhere.

We can also use VCAppExit for Visual CADD to do its own clean-up. All that is needed is:

  Private Sub Form_Unload(Cancel As Integer)

	'       clear mousedown and abort messages
        Call VCClearAlertApp(iError, SUFText.hWnd)
        Call VCAppExit(iError)

  End Sub

If the user aborts the user-tool while the Visual CADD interface has the input focus, Visual CADD will send an abort message. For example, if one tool is active when the user starts another tool (except snapping and tracking tools), then Visual CADD will abort the first tool. The user can also abort a tool by pressing ESCAPE. In either case, Visual CADD will send a KeyPress event with a key code of 27. (Note that the Visual CADD API Help file says the key code is 256. However, much experience indicates that Visual CADD sends a key code of 27.) User-tools should process the KeyPress event to check for user aborts. To be safe, the application should check for key codes 27 and 256.

In addition, your form can treat the ESCAPE key as an abort while the dialog box is open. The ASCII code for ESCAPE is 27, which has a predefined constant in Visual Basic named vbKeyEscape. If you check for abort codes as described above, your application will already process the ESCAPE key.

Thus, if either one of these codes is encountered, the user-tool should cancel as if the user had clicked the Cancel button on the form. This is done by using the BtnCancel_Click method.

Lastly, because KeyPreview was in effect for the SUFText form, if any key is processed by the form while one of the controls had the focus, it is not necessary for the control to also process it. This is done by setting the variable KeyAscii = 0.

The following beginning of the Form_KeyPress method does the above:

  Private Sub Form_KeyPress(KeyAscii As Integer)

	If ((KeyAscii = vbKeyEscape) Or (KeyAscii = 256)) Then

		'       abort and exit
                 BtnCancel_Click
                  KeyAscii = 0

As mention earlier, dialog boxes in OLE DLLs created with Visual Basic have some problems with Visual CADD. These problems are that the usual default functionality of the dialog boxes, such as tabbing between controls, the ENTER key selecting the default button, ALT+O selecting the OK button, etc., does not work. The dialog boxes are functional, but require all navigation by mouse pointing and clicks.

The following provides a workaround to these problems, which is completely general and may be copied to any other form requiring the fix.

The functionality of the TAB and ENTER keys will be handled by the Form_KeyDown method, so there is no need to pass these to the controls. The vbKeyTab and vbKeyReturn are predefined constants in Visual Basic. With that little addition, the Form_KeyPress method is complete.

	ElseIf ((KeyAscii = vbKeyTab) Or (KeyAscii = vbKeyReturn)) Then

		'       clear these keys
                 KeyAscii = 0

	End If
  End Sub

The KeyDown event and Form_KeyDown method are not required for the Visual CADD API - they are part of the workaround for the dialog box.

The first part of the fix is that ALT+O for &OK and ALT+C for &Cancel do not process correctly in a Visual Basic OLE DLL with Visual CADD. Thus, in the Form_KeyDown method, these key combinations must be used to run the associated BtnOK_Click and BtnCancel_Click. In addition, we need to declare a few variables for the rest of the fix:

  Private Sub Form_KeyDown(KeyAscii As Integer, Shift As Integer)

	'       needed due to problems with VB OLE DLLs and Visual CADD
        Dim NextTabIndex As Integer
        Dim TabStops As Integer
        Dim TabDirection As Integer
        Dim i As Integer

        '       capture the ALT+O
        If ((KeyAscii = vbKeyO) And (Shift And vbAltMask)) Then

         	BtnOK_Click

        '       capture the ALT+C
        ElseIf ((KeyAscii = vbKeyC) And (Shift And vbAltMask)) Then

                BtnCancel_Click

Next, ENTER does not select the default OK button. So, unless the Cancel button has the input focus, ENTER should be treated as BtnOK_Click.

	'       capture ENTER
        ElseIf (KeyAscii = vbKeyReturn) Then

         	If (Me.ActiveControl.Name = "BtnCancel") Then
                        BtnCancel_Click
                Else
                        BtnOK_Click
                End If

Last, the TAB keys do not navigate between tab stops. First, we need to find how many tab stops are set. Because not all controls on the form necessarily have the TabStop property, we need to find an alternative way of checking all the controls. Above, it was suggested that all tab stop controls have the Tag property set to some non-blank character, and an "x" was suggested. All controls have a Tag property, so this gives the program a way to find the tab stops.

The following code uses the Tag property to count the tab stops by checking all controls and counting each one which has a non-blank character in the Tag property.

	'       capture TAB and SHIFT+TAB
        ElseIf (KeyAscii = vbKeyTab) Then

                TabStops = 0

                For i = 0 To (Me.Controls.Count - 1)
                        If (Me.Controls(i).Tag <> "") Then
                                TabStops = TabStops + 1
                        End If
                Next i

The TAB key tabs forward, whereas SHIFT+TAB tabs backward.

		If (Shift And vbShiftMask) Then
			TabDirection = TabStops - 1
        	Else
                 	TabDirection = 1
        	End If

        	NextTabIndex = _
                	(Me.ActiveControl.TabIndex + TabDirection) Mod TabStops

Now, we can look through all the controls which are tab stops and set the focus to the next tab index.

		For i = 0 To (Me.Controls.Count - 1)
			If (Me.Controls(i).Tag <> "") Then
				If (Me.Controls(i).TabIndex = NextTabIndex) Then
					Me.Controls(i).SetFocus
                                        Exit For
                                   End If
                          End If
                  Next i
          End If
 End Sub

That completes the fix of the Visual Basic OLE DLL dialog problem with Visual CADD. As mentioned, it is unfortunate that we need to do this, but this fix is easy to copy to any form where needed and should not discourage anybody from using Visual Basic OLE DLLs with Visual CADD.

Now, back to the user-tool. After the user has been prompted by Visual CADD to place a point for the text, the form will wait for the MouseDown event, which indicates that the user has completed the point placement.

When the form gets the MouseDown event, it must get the location of the point from Visual CADD. The Visual CADD API routine VCGetUserToolLBDown does this, with the point being saved into the Point2D variable pt0. Note that the Single coordinates x and y passed to Form_MouseDown by Windows contain the window screen coordinates of the point and are not the same as the Point2D containing the Visual CADD real world coordinates.

  Private Sub Form_MouseDown(Button As Integer, Shift As Integer, _
  	x As Single, y As Single)

        '       get mousedown point
        Call VCGetUserToolLBDown(iError, pt0)

Once the placement location is set, all that remains is for the form to get the fraction from the user. This is done by now making the form visible and giving it the input focus. Recall that the form was already loaded when the user-tool was started, but was hidden due to setting the Visible property to False.

  	'       so show dialog box
        SUFText.Visible = True
        SUFText.SetFocus
  End Sub

With the SUFText form visible and having the focus, the user will enter the fraction and click either OK or Cancel buttons.

The OK button will create a Click event and will be handled by the BtnOK_Click method. With the point placed and the fraction entered, all that remains is to create single unit fraction text and allow the user to edit it.

The first step creates the text string with the special codes for single unit fractions, much like the Example of Parsing the Database and Modifying Entities. The difference here is that we know the fraction will have Numerator.Text in the numerator and Denominator.Text in the denominator.

  Private Sub BtnOK_Click()

  	Dim hEnt As Long
        Dim strSUF As String

        '       set SUF
        strSUF = Chr(1) & "N" & Numerator.Text & Chr(1) & "/" & _
        	Chr(1) & "D" & Denominator.Text & Chr(1) & Chr(1)
        Call VCSetTextString(iError, strSUF)

Having set the text string, a new text entity can be added at the placement point. The VCAddTextEntityBP routine will add the text entity.

Entities may be added to drawings, hatches and fills, or symbols. The first parameter after the error code indicates where the entity is being added. The NONDEFENTITY constant is defined in the VCTYPE32.BAS module as -1 (negative-one), which means the entity is being added to the drawing.

The placement point is given by the last parameter, which is the Point2D variable pt0 retrieved by the Form_MouseDown method.

Note that it is necessary to use the BP version of VCAddTextEntityBP, because the Point2D parameter is a user-defined type.

  	'       add text entity
  	Call VCAddTextEntityBP(iError, NONDEFENTITY, pt0)

After adding the text entity, the user-tool will allow the user to edit it using the same tool as Text Editor (TE). To do this, the entity must be selected. Before selecting it, we must deselect all other entities. The VCClearSelection routine does this.

  	'       clear any previous selections
  	Call VCClearSelection

To select the added text entity, we will make it current, then select it. To make it current, we must find it in the database. But, because new entities are always added to the end of the database, we know that the new entity is the last entity. Using VCLastEntity, we can get the entity handle of the new text entity. (Refer to the previous discussion of VCLastEntity for errata to the Visual CADD API Help file.)

Using this entity handle, VCSetCurrentEntity will make it the current entity. Finally, the VCSetCurrentSelected will select the current entity.

  	'       select new text entity
  	Call VCLastEntity(iError, hEnt)
  	Call VCSetCurrentEntity(iError, hEnt)
  	Call VCSetCurrentSelected(iError)

Once selected, VCEdit will start the Edit (ED) tool, which for text entities is the Text Editor (TE) tool.

  	'       let user edit text
  	Call VCEdit

VCEdit is in the VCTOOL32.DLL and is declared in the VCTOOL32.BAS module. This latter module must be added to our project in order for Visual Basic to have the proper declaration for this routine.

When the user is done editing the text with the single unit fraction, the tool has successfully completed. We can now redraw the drawing and end the undo level.

  	'       redraw and end

  	Call VCInvalidateRect
  	Call VCEndOperation(iError)

To return to Visual CADD, we need to unload the SUFText form.

  	'       unload form
  	Unload SUFText
  End Sub

The Cancel button uses VCAbortOperation to abort the undo, instead of VCEndOperation, which would complete the undo level.

  Private Sub BtnCancel_Click()

  	'       redraw and abort
  	Call VCInvalidateRect
  	Call VCAbortOperation(iError)

  	'       unload form
  	Unload SUFText
  End Sub

The final step is to create the OLE DLL using the File, Make OLE DLL File command in Visual Basic. Name your DLL VBVCADD.DLL, for Visual Basic - Visual CADD, and save it in your VBVCADD project folder. If you are adding this new function to the project created by a previous tutorial, you may replace the original VBVCADD.DLL with your new one.

To run your OLE DLL, include the following in your CMDEXT.DEF (recommended) or Assign Script (AS), where the first line is omitted for scripts.

  SUFText,SUT, ,Single Unit Fraction Text, Single Unit Fraction Text,
 	DllName;OLEVCADD.DLL;DllFunName;VBOleDll;
 	DllCmdLine;VbVCadd.General|VBSUFText|;DllRun;

Note that it is not mandatory that the above be placed in the CMDEXT.DEF. However, when a user-tool starts, the VCSetUserTool specifies a native command (in this case SUFText) which is looked-up in the CMDEXT.DEF. If found, the tool description (Single Unit Fraction Text) is displayed in the Visual CADD status bar when the user-tool runs.

From Visual CADD, running SUFText (SUT) will prompt the user to place text, just as Text Line (TL) or Text Editor (TE). After placement, a dialog box will accept the fraction to be used in the single unit fraction. Then, the user is allowed to edit the text using Text Editor (TE). The user can add text and, if necessary, move or copy additional fractions by using cut-and-paste (CTRL+C, CTRL+X, and CTRL+V).

Programming Index | VB and VC++ Setup | Example 4 Overview | Programming Links
  UFText|;DllRun;

Note that it is not mandatory that the above be placed in the CMDEXT.DEF. However, when a user-tool starts, the VCSetUserTool specifies a native command (in this case SUFText) which is looked-up in the CMDEXT.DEF. If found, the tool description (Single Unit Fraction Text) is displayed in the Visual CADD status bar when the user-tool runs.

From Visual CADD, running SUFText (SUT) will prompt the user to place text, just as Text Line (TL) or Text Editor (TE). After placement, a dialog box will accept the fraction to be used in the single unit fraction. Then, the user is allowed to edit the text using Text Editor (TE). The user can add text and, if necessary, move or copy additional fractions by using cut-and-paste (CTRL+C, CTRL+X, and CTRL+V).

Programming Index | VB and VC++ Setup | Example 4 Overview | Programming Links