Visual Basic and Windows 3.1 Extensions

Custom controls are central to Windows programming, particularly pencentric development.


May 01, 1992
URL:http://www.drdobbs.com/windows/visual-basic-and-windows-31-extensions/184408768

Figure 1


Copyright © 1992, Dr. Dobb's Journal

Figure 1


Copyright © 1992, Dr. Dobb's Journal

Figure 2


Copyright © 1992, Dr. Dobb's Journal

Figure 2


Copyright © 1992, Dr. Dobb's Journal

MAY92: VISUAL BASIC AND WINDOWS 3.1 EXTENSIONS

VISUAL BASIC AND WINDOWS 3.1 EXTENSIONS

Custom controls are key to pencentric development

This article contains the folowing executables: PENCKBK.ARC

Moshe Lichtman

Moshe is a product manager for Microsoft and the coauthor of the Complete Guide to the C Language (Hod-Ami Publishing, 1988), a textbook published in Israel. He can be contacted at One Microsoft Way, Redmond, WA 98052.


You've undoubtedly noticed a number of recent DDJ articles on programming Windows applications using various flavors of Basic--Visual Basic, GFA Basic, and the like. In this article, I carry that discussion into the realm of pen-based application development by presenting an overview of the pen extensions to the Microsoft Windows 3.1 API. I then describe how custom controls make this new API accessible to Visual Basic applications, and how they can turn conventional Visual Basic programs into pencentric applications.

Windows 3.1 Pen Extensions

"Windows for Pens" is the name given to a set of extensions to Version 3.1 of Microsoft Windows. Windows for Pens adds about 70 new functions that support pen-based applications to the existing 600-plus functions in the Windows 3.1 API.

As Figure 1 illustrates, Windows for Pens functions can be divided into six groups: Pen Interface, Pen Data, Recognizer, Pen module, Drivers, and Dictionary. Figure 1: Windows for Pens software architecture

The Pen Interface module includes most of the functions required to build pencentric applications. It consists of 32 functions, the most important being Process-Writing(). This is a high-level recognition function which handles the entire recognition process. Your application passes control to this function, which tracks pen motion and then returns a set of recognized characters. For flexibility, the Pen Interface module also allows low-level access to the recognition and data-conversion functions. The module also includes training functions that facilitate contextual training and virtual event handlers that add pen compatibility to existing Windows applications.

Pen Data functions include all the logic for ink and pen data manipulation and data transfer between applications. Functions in this module provide device-specific information (for example, stylus pressure, angle, and proximity to surface), data compression, and additional data-conversion functions.

Most of the functions in the remaining four modules are used by specialized applications and by Windows for Pens device drivers. The Custom Recognizer module includes functions which must be supported by any Windows for Pens recognizer: configuration and initialization, recognition, and training. The Pen and Drivers modules include functions which pen and display drivers must support or use in addition to the functionality required from standard Windows drivers.

The Dictionary module includes a single function, DictionaryProc, which enables application developers to build their own dictionaries in addition to the standard dictionary of language words provided with Windows for Pens. Dictionaries can significantly enhance recognition in situations in which the input to be recognized can be constrained to a set of words.

The pen extensions (excluding the recognition module) are reasonably compact in size, under 130 Kbytes. Windows for Pens can run on a variety of platforms, spanning a range of form factors and hardware configurations. In conjunction with the forthcoming ROMable Windows, Windows for Pens will run comfortably on diskless platforms with a total memory size (both ROM and RAM) of less than 2 Mbytes.

Designing a windowing environment to comfortably accommodate small form factors is not a simple task. The answer is not just shrinking everything to fit the smaller screen. The standard Windows environment does not work well in this situation. However, the solution is not to adopt a new rigid paradigm that forces a specific metaphor on application developers. Rather, the system software should be modular and open enough to allow developers to easily tailor their applications to target markets and platforms. Windows for Pens provides this flexibility, as well as support for power management and Flash memory, both of which are essential to small tablet and hand-held computers.

Architectural flexibility is also required in the area of handwriting recognition. The embryonic nature of handwriting-recognition technology and the need to support a multitude of different languages and markets dictates a modular design. The Windows for Pen API enables ISVs and handwriting-recognition vendors to build custom recognizers as well as support foreign languages. In the future, recognition of cursive writing and voice input will also fit into this architecture.

Visual Basic and Pens

To create a standard Windows application using Visual Basic, you interactively design its interface by dragging and placing GUI elements, known as "controls," onto a layout form. You specify the behavior of your application by attaching Visual Basic code to these controls. Programmers can extend the default functionality in Visual Basic by means of custom controls. Custom controls are similar to Windows controls and are typically written in C, following the specifications in the Visual Basic Control Development Kit (CDK). Once created, custom controls are basically indistinguishable from controls included with the product.

The pen-oriented custom controls added to Visual Basic provide a high-level, interactive way of creating pencentric programs. Although some pencentric applications need to use the full Windows for Pens API (for example, a calligraphy program that relies on device-specific information such as stylus pressure and angle), the needs of most pencentric applications can be satisfied with less functions. These commonly used functions are found in the Windows for Pens custom controls and are contained in the file pencntrl.vbx. Currently, this file is available only as part of the Windows 3.1 Pen SDK, but future versions of Visual Basic will include these controls as an integral part of the development environment.

The BEdit and HEdit Controls

The BEdit and HEdit custom controls replace the standard Visual Basic text-input controls. HEdit is a pen-enhanced version of the text-box control. It supports most of the regular text-box properties and adds the handwriting and inking capabilities from the Windows for Pens API. When running, it accepts handwriting input and displays the corresponding recognized text. Many of the initial pen applications will be form based, so the HEdit has an additional BorderStyle setting, Underline, which applies only to single-line controls.

BEdit, or "boxed edit" control, is a superset of HEdit and provides the application with comb or box style guides that accept pen input. Each segment or box accepts only a single character of input. The BEdit control is used to work around the limitations of today's recognition algorithms. BEdit provides important baseline and segmentation information to the recognizer. By dividing up handwritten text into single characters placed one at a time into box or comb guides, users write more neatly, and recognition accuracy is significantly enhanced. BEdit properties control the size of the BEdit cells, their shape (box or comb), and the number of rows and columns in a certain control. BEdit is for fields which accept a predefined text length. For example, phone numbers will typically not exceed 15 characters.

Pencentric Properties

Unlike typed text, the size of which is controlled by the application, handwriting size depends solely on user habits. Users tend to write larger because of the limited resolution and display quality found in today's pen computers. Pen applications address this problem by allowing inking in areas that extend beyond the displayed input box. This area is sometimes called the "gray area." Its size is controlled via the "Inflate" properties, of which there are four: InflateBottom, InflateLeft, InflateRight, and InflateTop. The gray area becomes active only when the associated control receives focus (which indicates that the user has started inking inside the visible boundary). In delayed-recognition mode, the gray area is not active: ink can then be drawn only inside the visible boundary.

It's no secret that present handwriting-recognition engines, even with accuracy levels as high as 95 percent, are far from perfect. Pencentric applications should be designed to minimize text entry and to utilize contextual information during the recognition process. One popular method for enhancing recognition accuracy is to constrain the character set for a particular field. For example, there is no point in allowing alphabetic characters in a social-security number field. By constraining the allowed character set in the social-security number field to numerical only, we avoid misrecognizing "Z" for "2" and "O" for "0" (zero).

In Visual Basic, you can use the CharSet property, which accepts the settings: ALC_DEFAULT, ALC_LCALPHA, ALC_UCALPHA, ALC_NUMERIC, ALC_PUNC, ALC_MATH, ALC_MONETARY, ALC_OTHER, and ALC_WHITE. These settings can be ORed together and are controlled either at design time via a custom dialog, or at run time.

Ink is an exciting feature unique to pen applications. The ability to capture, store, manipulate, and display ink opens up new application categories as well as adding a new dimension to existing "text-centric" applications. Visual Basic supports inking via the DelayedRecog property. When set to True, all writing in the control remains as ink until the property is set to False. When it is changed from True to False, the OnTap property is examined. If OnTap is True, recognition of the collected ink will take place when the user taps on the control with the pen. The pen custom controls incorporate two new events: the RcResult and the Update. The RcResult event occurs whenever the control receives recognition results from the recognizer. The returned result is a pointer to the RcResult structure (Recognition Context), which forms a central part of the Windows for Pens API. The RcResult structure contains a symbol graph that describes the possible results from the ink, a handle to the ink, and the best guess at the symbols. If DelayedRecog is True, the RcResult structure does not contain any information about recognized symbols.

The Update event occurs before the control redraws the data. This differs from the Change event, which redraws the data before it occurs. Update can be used to format data so that flashes do not appear.

Aside from allowing quick erasing of the ink via the EraseInk method, Visual Basic currently does not provide sophisticated ink-handling methods. To add advanced functions such as ink selection, resizing, and editing you must access the internal ink data structures and the underlying Windows for Pens API. You can do this with Visual Basic's facility for declaring external DLL calls and by using the handles to the ink and window structures provided by the hInk and hWnd methods. I'll discuss these later.

An Example Application

PenCheckbook is a program that started out as a regular Visual Basic application. I converted it into a pen application by replacing the standard controls with pen custom controls.

The background consists of a bitmap of a check. This bitmap was first drawn with Paintbrush, and cut-and-pasted into a picture field in the main program form. In the nonpen version of the program, the main form includes five Edit Controls to handle user input: PayToCtrl, AmountCtrl, AmountStrCtrl, RemarksCtrl, and SignatureCtrl. Three other fields are display-only: DateLabel, CheckNumLabel, and CurrencyLabel, which display, respectively, the current date, a revolving check number, and the relevant currency. These fields are controlled by the application but do not accept user input.

Figure 2 shows the main program display after conversion for pen input. Captions have been added to identify the fields.

Figure 2: A PenCheckbook check

The application includes a global data structure that holds information about the last check. In the conversion process, I modified this structure to hold ink information and to be able to store and retrieve this information from a file. However, I've not yet implemented file storage and retrieval. For now, the items in the File and Edit menus (Save As, Open, Undo, and Clear) are merely placeholders.

The Options menu includes the functions Balance, Hide, and Show Last. The Balance function loads a second form, which displays the current balance and enables the user to modify it. The current balance is automatically updated when a check is signed by the user. If the user tries to write an amount that exceeds the current balance, the application warns the user and cancels the check. The menu bar can be removed via the Hide function. Tapping on the form brings back the menu bar. This can be useful in pen applications which can be controlled via gestures rather than menu functions. It supports a less cluttered user interface while retaining the option to display additional controls for training purposes or advanced functionality. Lastly, the Show Last function displays the last check signed by the user.

Converting Checkbook to Use Pens

In modifying Checkbook to accept pen input, the standard edit controls were replaced with pen custom controls. Consequently, the PayToCtrl, AmountStrCtrl, and RemarksCtrl fields can process both handwriting and gestures. The original AmountCtrl field was replaced by a boxed edit field which recognizes numbers and gestures only. The BalanceCtrl field in the Balance form will be converted to a BEdit on-the-fly (when the user presses the Change button). This whole process requires adding less than ten lines of code. The new SignatureCtrl field accepts the user's signature and retains it as ink.

Replacing the standard controls with pen controls is a slightly tedious task, because there is no automated way to do it. You have to delete the old control, drag the new HEdit or BEdit control, and redefine various properties such as BorderStyle, BackColor, and CtlName. During this process you may notice a number of new properties. The CharSet property enables you to define the characters accepted by the input field; restricting the character set substantially improves recognition.

When adding the SignatureCtrl field, I set the DelayedRecog property to True. This results in an ink field which captures stylus input and can later recognize ink if the user taps on the field when the OnTap property is set to True. The program uses the signature to "lock" a certain check so that users cannot change the amount on the check once it has been signed.

Modifying the AmountCtrl and BalanceCtrl fields (located on the second form) requires more changes. The single edit control is replaced by two BEdit controls for Dollars and Cents. You will need to adjust the Cell and Comb properties to achieve the best space utilization without compromising usability. The names of the controls now become CentCtrl, DollarBalanceCtrl, and CentBalanceCtrl. I used the CharSet property to restrict input to numerical values (and gestures).

At this stage in its evolution, the Checkbook program is ready for pen input. Users can write and sign checks on screen similar to the way they would in a paper checkbook. Standard gestures can be invoked to edit and manipulate the various fields.

After using the application for a while, you'll realize that certain layout changes make life easier. As mentioned before, handwriting is typically larger than printed text. The controls built for keyboard input may be too small and result in loss of ink when the pen exits the border of the control. By using the Inflate properties of HEdit and BEdit, you can adjust the size and location of the fields to fit the new method of input. The Inflate properties don't change the visible borders of the controls; they affect only the gray area which surrounds them. Thus, the user can write beyond the visible borders without losing ink.

Accessing the Underlying API

Visual Basic provides several hooks that enable developers to tap into the full functionality supported by the Windows for Pens API. These include the RcResult and Change events and the hInk and hWnd handles.

The RcResult event occurs when a BEdit or HEdit control attempts to recognize the ink. This event can be used as a trigger to create locking form fields in form-based applications where a signature validates the data in the form. Once the user stops inking in the signature field, your program can lock all the input fields in the form so no changes can be made. To lock a field, merely set the Enabled property to False. In PenCheckbook, once the user has signed the check, the only way to unlock the input fields is by pressing the Pay button or selecting Clear in the Edit menu. In both cases, the input field will be initialized.

Because the RcResult event occurs before the recognition results affect the display, it can be used to trap custom gestures. Listing One contains declarations from the global declarations file of PenCheckbook. This file is basically the Visual Basic version of PENWIN.H. These declarations are used in processing the RcResult event.

Listing Two shows the ProcessGestures subroutine. ProcessGestures() does the same thing as the "Hide" function in the Options menu, but it is invoked with a gesture. If the user draws the "Circle H" gesture in the edit control, this routine will hide the menu bar. Process_Gestures() is defined in the global module and is called by each edit control when the RcResult event occurs.

As previously mentioned, Checkbook lets you store and display the last check paid. When SignatureCtrl becomes an ink field, the CheckRec global structure needs to be modified to hold a pointer to the ink structure rather than the signature text. Listing Three contains declarations and code samples showing how hInk, hWnd, and the Windows for Pens API enable the ink to be stored in memory and retrieved later on.

Conclusion

By combining Visual Basic with pen custom controls, you get a tool that lets you create simple pen applications quickly, and at the same time implement truly pencentric programs via the underlying Windows for Pens API.

DDJ



_VISUAL BASIC AND WINDOWS 3.1_
by Moshe Lichtman


[LISTING ONE]


Type RcResult                   ' RcResult structure
    SYGraph As syg              ' Symbol Graph element
    wREsultsType As Integer     ' Status of RcResult handling
    cSyv As Integer
    lpsyv As Long
    hSyv As Integer
    nBaseLine As Integer
    nMidLine As Integer
    hPenData As Integer
    rectboundink As RECTSHORT
    pntEnd As PointShort
    lprc As Long
End Type

Type syg                                ' Symbol Graph structure
    rgpntHotSpotsArray As PointArray    ' "hot spot" array
    cHotSpot As Integer
    nFirstBox As Integer
    lRecogVal As Long
    lpSye As Long                       ' Pointer to first Symbol Element
    cSye As Integer                     ' Number of Symbol elements
    lpSyc As Long
    csyc As Integer
End Type

Type SYE                                ' Symbol element structure
    Syv As Long                         ' Symbol Value
    lRecogVal As Long
    cl As Integer
    iSyc As Integer
End Type

'The following two functions copy data to/from Visual Basic strings
'and from/to memory pointed by long pointers.
Declare Sub VBTypeToCPointer Lib "PENCNTRL.VBX" (lpSrc As Any, ByVal
lpDest As Long, ByVal cb As Integer)
Declare Sub CPointerToVBType Lib "PENCNTRL.VBX" (ByVal lpSrc As Long,
lpDest As Any, ByVal cb As Integer)





[LISTING TWO]


Sub Process_cGestures
    Dim VBrc As RcResult
    Dim SyeTable() As SYE                ' array of Symbol elements
    CPointerToVBType ByVal RcResult, VBrc, 80  'copy RcResult struct to VB var
    NumOfSymbols% = VBrc.SYGraph.cSye    ' get number of Symbol elements
    lpSye& = VBrc.SYGraph.lpSye          ' pointer to first Symbol Element
    ReDim SyeTable(NumOfSymbols%)        ' re-define Symbol array
                                         ' copy Symbol elements
    CPointerToVBType ByVal lpSye&, SyeTable(0), NumOfSymbols% * 12
    If (NumOfSymbols% = 1) Then          ' process only if single gesture
        Syv& = SyeTable(0).Syv
        SyvType& = Syv& \ &H10000        ' get Symbol type. Is it a gesture?
        If (SyvType& = SYVHI_GESTURE) Then ' if so, is it a circle-H gesture?
             If (Syv& = (SYV_CIRCLELOA + Asc("h") - Asc("a"))) Then
                HideCmd_Click            ' hide menu-bar
                VBrc.wREsultsType = VBrc.wREsultsType Or RCRT_ALREADYPROCESSED
             End If                      ' mark to prevent further processing
        End If
        VBTypeToCPointer VBrc, ByVal RcResult, 80   ' update RcResult
    End If
End Sub





[LISTING THREE]


' DuplicatePenData() takes a handle to ink (hPenData or hInk) and returns a
' new hInk/hPenData that has a copy of the ink. NOTE: Whenever this function
' is used, a GlobalFree() call must be issued to release the memory.
Declare Function DuplicatePenData Lib "PENWIN" (ByVal hPenData As
Integer, ByVal gMemFlags As Integer) As Integer

' SendMessage() is the message sending function of the Windows 3.x API.
Declare Function SendMessage Lib "USER" (ByVal hWnd As Integer, ByVal
wMsg As Integer, ByVal wParm As Integer, ByVal lParam As Any) As Long

Sub PayButton_Click ()
    Amount! = Val(DollarCtrl.text) + Val(CentCtrl.text) / 100
    balance = balance - Amount!
    If balance < 0 Then
         Beep                               ' warn user if insufficient funds
         MsgBox "Insufficient Funds - Check Cancelled", 0, "ERROR!"
         balance = balance + Amount!
    Else
         check.NumField = CheckNumLabel.Caption    ' store last check, remarks
         check.RemarksField = RemarksCtrl.text     ' and store ink field
         check.SignatureField = DuplicatePenData(SignatureCtrl.hInk, ByVal 0)

    PayToCtrl.text = ""                           ' initialize input fields

    PayButton.Enabled = False
End Sub

Sub ShowLastCmd_Click ()         ' "Show Last" command function

     lParam = check.SignatureField           ' Force integer into long
     CheckNumLabel.Caption = check.NumField  ' restore last check and
                                             ' retrieve ink from memory
     lRet = SendMessage(SignatureCtrl.hWnd, WM_HEDITCTL, HE_SETINKMODE, lParam)
End Sub


Copyright © 1992, Dr. Dobb's Journal

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.