Your full service technology partner! 
-Collapse +Expand
Search Paradox Group:

-Collapse +Expand Paradox To/From
-Collapse +Expand Paradox Store

Prestwood eMagazine

January Edition
Subscribe now! It's Free!
Enter your email:

   ► KBDesktop Data...Paradox & Ob...P9 Book: Pow...   Print This     
  From the August 2015 Issue of Prestwood eMag
Paradox P9 Book: Power Programming:
Power: Chapter 16, Handling the Keyboard
Posted 16 years ago on 3/26/2003 and updated 1/29/2011
Take Away:

Chapter 16, "Handling the Keyboard" from Paradox 9 Power Programming by Mike Prestwood.


This short but important chapter will introduce you to handling user input via the keyboard. Key concepts covered include what events are triggered when a user presses a key and how to limit the user's input. Also covered is a Paradox trick for interrupting a loop and sendKeys().

The Path of a Keystroke

A keycode is a code that represents a keyboard character in ObjectPAL. A keycode can be an ASCII number, an IBM extended keycode number, or a string that represents a keyname known to Paradox. When a user presses a key on the keyboard, one of two things occurs. Either Windows processes it, or Paradox processes it. Windows processes it, for example, when the sequence ctrl-esc is used. When Paradox processes it, the keyPhysical event always sees it, and in the case of a character, the keyPhysical event passes the event to the keyChar event (see Figure 16-1).

Figure 1: The path of a key event

The steps to trapping a keystroke are as follows:

  1. Decide whether you need to trap for a character or virtual key. If you want to trap for a character such as a, A, b, B, 1, !, or @, then use either the keyPhysical event or the keyChar event. If you want to trap for virtual keys such as F1 or esc, then you must use the keyPhysical event.
  2. Decide at which level you need to trap for the key press. The two usual places are the form’s prefilter or directly on the field.
  3. Inspect the eventInfo packet with either char() or vChar() to trap for the keystroke. If case does not matter, then use vChar(). If case does matter, then use char().

Note: As long as a field has focus, keystrokes do not bubble because the field uses up the keystroke. Therefore, the two best choices to trap for keystrokes are the form’s prefilter or on the field itself.

Using KeyEvents Methods and Procedures

The eventInfo passed to both the keyChar and keyPhysical events is of type KeyEvent. This means you can use the KeyEvent methods and procedures to enable you to get and set information about keystroke events. The keyEvent object type inherits some methods and procedures from the event object type. For example, the following traps for ctrl-shift-F1 using several keyEvent methods.

; Field :: keyPhysical
method keyPhysical(var eventInfo KeyEvent)
;Start by disabling all keys, then we'll
;either do our processing or enable the
;keystroke at the end of this method.

case eventInfo.isControlKeyDown() :
case eventInfo.isShiftKeyDown() :
case eventInfo.vCharCode() = VK_F1 :
msgInfo("", "You pressed cntrl + shft + F1")
otherwise :
;Another key was pressed.
otherwise :
; No shift keystroke.
otherwise :
; No control keystroke.

enableDefault ;Allow all other keystrokes.

Refer to KeyEvent Type in the ObjectPAL Reference help file for a complete list of methods.

Interrupting a Loop

Sometimes you may want to give the user the option of interrupting a loop. For example, if you are scanning through a large table, you could allow the user to abort the procedure in midstream. This type of control adds a touch of professionalism to your application. The following example demonstrates how you can enable the user to interrupt a loop by pressing the esc key.

Suppose that you want to loop to 1,000 and display the counter in the status bar as the loop increments. The twist on this example is that you need to enable the user to press esc to interrupt the loop. This example uses the form’s prefilter with vChar() to trap a keystroke.

Step By Step

  1. Create a new form and place a button labeled Count to 1000 and a text box on the form (see Figure 16-2).
  2. Figure 2: Set up form

  3. Add line 3 to the Var window of the form.
  4. 1: ;Form :: Var
    2: Var
    3: lFlag Logical
    4: endVar

  5. Add lines 5–7 to the keyPhysical event of the form.
  6. 1: ;Form :: keyPhysical
    2: method keyPhysical(var eventInfo KeyEvent)
    3: if eventInfo.isPreFilter() then
    4: ;This code executes for each object on the form
    5: if eventInfo.vchar() = "VK_ESCAPE" then
    6: lFlag = True
    7: endIf
    8: else
    9: ;This code executes only for the form
    10: endIf
    11: endMethod

  7. Alter the mouseDown event of the text box as follows:
  8. ;Text :: mouseDown
    1: method mouseDown(var eventinfo MouseEvent)
    2: lFlag = True
    3: endMethod

  9. Add lines 3–16 to the pushButton event of the Count to 1000 button. Line 4 declares a variable private to a method for use in the for loop (lines 9–16). Line 7 sets the flag to False in case the user presses esc, setting the flag to True. Line 10 displays the value of siCounter in the status bar. Line 11 sleeps for the minimum amount of cycles (about 52 milliseconds), which is plenty of time to yield to Windows. This enables the esc key to sneak in. Line 12 checks whether the flag has changed to True. If the flag is True, line 13 displays a message, and line 14 quits the loop.
  10. 1: ;Button :: pushButton
    2: method pushButton(var eventInfo Event)
    3: var
    4: siCounter SmallInt
    5: endVar
    7: lFlag = False
    9: for siCounter from 1 to 1000
    10: message(siCounter)
    11: sleep()
    12: if lFlag = True then
    13: message("Counting interrupted")
    14: quitloop
    15: endIf
    16: endFor
    17: endMethod

  11. Check the syntax, run the form, and click the button. As the computer counts to 1,000, the counter is shown in the status bar. Press esc or click the text box to interrupt the loop (see Figure 16-3).

Figure 3: Finished example of interrupting a loop

Note: Sometimes programmers use sleep(1) to indicate they want to release control to windows, but for the least amount of time. In place of sleep(1), you can use sleep() without a parameter--sleep(). In that case, Windows automatically permits about two events to occur.

Using keyPhysical

As already discussed, use the keyPhysical event when you want to trap for all keyboard keys. Use the keyChar event when you want to trap for only characters that are printable to the screen.

Suppose that you created two field objects: field1 and field2. You want field2 to echo whatever you type into field1—including backspace, delete, and enter—as though you were typing directly into field2. How do you do this?

A problem that often confronts users is that values aren’t committed to the field until endMethod. Remember that the default behavior occurs last in a method. Therefore, when you use the keyPhysical and keyChar events, invoke the default behavior to commit the last keystroke, as in the following example:

1: ;Field1 :: keyPhysical
2: doDefault
3: field2.value = self.value

Limiting Keyboard Input

If you want to limit the user’s input, use either the keyChar or keyPhysical events of the input object. If you want to limit the user’s input to characters, use keyChar. If you want to control all keystrokes, use keyPhysical.

Suppose that you want to limit the user’s input to a list of specified keys. The technique presented here can be used to control any keys.

Step By Step

  1. Create a form with a single undefined field (see Figure 16-4).
  2. Figure 4: Setup form for limiting a user’s input example

  3. Alter the keyPhysical event of the field as follows:
  4. 1: method keyPhysical(var eventInfo KeyEvent)
    2: var
    3: sChar String
    4: endVar
    6: disableDefault
    8: sChar = eventInfo.char()
    10: switch
    11: case eventInfo.vCharCode() = VK_DELETE : enableDefault
    12: case eventInfo.vCharCode() = VK_BACK : enableDefault
    13: case eventInfo.vCharCode() = VK_F8 : enableDefault
    14: case sChar >="A" and sChar <="z" : enableDefault
    15: case sChar >="0" and sChar <="9" : enableDefault
    16: case sChar = "." : enableDefault
    17: case sChar = "," : enableDefault
    18: case sChar = " " : enableDefault
    19: endSwitch
    20: endMethod

  5. Check the syntax, save the form as ONLYKEYS.FSL, and run it. Type some characters. Be sure to try numbers, letters, and special characters (see Figure 16-5).

Figure 5: ONLYKEYS.FSL Limits the user’s input to numbers, letters, and spaces

This is a good routine to turn into a custom method that you can call whenever you need to limit user input. Alter this routine in other ways to suit your needs—perhaps develop three or four custom methods for limiting user input. You can’t trap for everything, however. Some keys and key combinations are reserved by Windows, such as ctrl-F4.

Character Sets

You can’t store all characters in a Paradox table. This is a limitation of current computer technology. The issue is what Paradox should store to a table. For example, should it store Windows ANSI characters or OEM DOS characters? ANSI is an acronym for American National Standards Institute. The ANSI set consists of 8-bit codes that represent 256 standard characters, such as letters, numbers, and symbols. The ANSI set is used by Windows applications. extended ASCII is a character set designed by IBM. IBM extended the standard ASCII set from 7-bit codes to 8-bit codes and added more characters. The extended ASCII set contains 256 characters.

As a Windows product, the Paradox table structure has to be able to store Windows ANSI characters. Paradox supports dBASE and Paradox tables, however, and traditionally these table structures store OEM DOS characters. Therefore, Paradox must be able to deal with both character sets: the character set traditionally used by other products that used the table structures before Paradox and the character set used by Windows. In Paradox, the table language driver determines the character set. The problem is that although Microsoft controls both DOS and Windows, the two character sets, OEM and ANSI, are incompatible. You must decide between the two when you create your table.

One solution is to use the strict translation option of the Link tool. When strict translation is checked (the default), only the first 128 characters are stored. If you uncheck it, you enable your users to add characters that may not be supported by a different table language. There are disadvantages, however. For more information, refer to the online help on strict translation.

Using sendKeys()

The sendKeys() procedure sends keystrokes to the Windows message queue The syntax for sendKeys() is as follows:

sendKeys(const keyText String [, const wait Logical] ) Logical

The wait argument indicates whether to send the keys immediately (True), or to wait until the current method has finished (False). In most cases, False is the preferred setting.

Now type in an example to make sure you fully understand the principles of using sendKeys(). For example, the following simulates a user selecting File | Open | Table, typing Customer, and pressing enter. Type in the fourth line into the pushButton event of a button.

1: ;Button :: pushButton
2: method pushButton(var eventInfo Event)
3: ;% = ALT key.
4: sendKeys("%FOTCustomer{ENTER}", False)
5: endMethod

sendKeys Tips:

  1. Use sendKeys as a last resort because sendKeys is and has never been super reliable. This is true not just for Paradox but also for other environments such as Access and VB Classic.  You almost always can find another way to accomplish the task.
  2. In place of {ENTER}, you can use a tilde ~.
  3. Use a False value for the optional wait parameter. Windows sometimes stops responding when the wait parameter is set to true (from the ObjectPAL help). The default is True so it is highly recommended to use False.
  4. You may want to try {delay 100} at the beginning of your string to tell ObjectPAL to delay a 10th of a second "between" keystrokes.
  5. If you are using Paradox 9, make sure you upgrade to SP3 or SP4. Sendkeys does not work on at least the initial developer edition.
  6. For Vista/Win7, disable UAC.

Here is a working example:

method pushButton(var eventInfo Event)
sendKeys("{delay 50}%FOTCustomer~", False)

I have found that the {delay 50} is required at least for Windows 7 64bit.

Refer to the ObjectPAL Reference help file for more information.

Vista / Windows 7 and sendKeys

SendKeys is a Windows feature that Paradox, Access, VB Classic and other older development environments make available with a programming command. Starting with Vista, Microsoft has disabled SendKeys by default -- a security precaution against hackers. Also, many antivirus programs will enforce the disabling of SendKeys. So, to use SendKeys going forward, you have to disable UAC and make sure your antivirus program does not enforce the disabling of SendKeys.


Trapping for keystrokes is done in either the keyChar or keyPhysical events. The keyPhysical event passes the eventInfo to keyChar only if a printable character was pressed. The best location to trap for keys is either directly on the field in question or at the form’s prefilter. If you wish to create a generic keyboard trapping routine, consider passing the eventInfo in the form’s prefilter to a library routine that processes it.

Linked Message Board Threads

 Sendkeys in Paradox 11 in Vista in ObjectPAL MB Topic (15 replies)
 paradox 10 syntax printing in ObjectPAL MB Topic (1 replies)
 Sendkeys in ObjectPAL MB Topic (18 replies)
 Usefull code for AUTOMATIC form printing in ObjectPAL MB Topic (0 replies)
 sendKeys problem in ObjectPAL MB Topic (2 replies)


Share a thought or comment...
First Comment
Comment 1 of 4
Dear Sirs I am surprised. I am starting to change my old system to PDOX 9 and inmediatly arose the sendKeys() problem. I am inserting also your lines with these topic: sendKeys ("%FOxxxx~") but nothing happens. I believe the problem is becouse sendkey() does not recognize the desktop as the active object. How to solve ?? Thanks in advance Regards Wilhelm Leskovsek - Chile
Posted 16 years ago

Comment 2 of 4

Lots of reasons why SendKeys might not work. Try the following tips:

  1. Use sendKeys as a last resort because sendKeys is and has never been super reliable. This is true not just for Paradox but also for other environments such as Access and VB Classic.  You almost always can find another way to accomplish the task.
  2. In place of {ENTER}, you can use a tilde ~.
  3. Use a False value for the optional wait parameter. Windows sometimes stops responding when the wait parameter is set to true (from the ObjectPAL help).
Posted 16 years ago

Comment 3 of 4

this helped me with a little nagging problem with our nightly process. thanks !!!

Posted 8 years ago

Latest Comment
Comment 4 of 4

Awesome Troy. Great to hear.

Posted 8 years ago
Write a Comment...
Sign in...

If you are a member, Sign In. Or, you can Create a Free account now.

Anonymous Post (text-only, no HTML):

Enter your name and security key.

Your Name:
Security key = P1186A1
Enter key:
KB Post Contributed By Mike Prestwood:

Mike Prestwood is a drummer, an author, and creator of the PrestwoodBoards online community. He is the President & CEO of Prestwood IT Solutions. Prestwood IT provides Coding, Website, and Computer Tech services. Mike has authored 6 computer books and over 1,200 articles. As a drummer, he maintains and has authored 3 drum books. If you have a project you wish to discuss with Mike, you can send him a private message through his PrestwoodBoards home page or call him 9AM to 4PM PST at 916-726-5675 x205.

Visit Profile

 KB Article #100211 Counter
Since 4/2/2008
   Contact Us!
Have a question? Need our services? Contact us now.
--Mike Prestwood

Call: 916-726-5675


Go ahead!   Use Us! Call: 916-726-5675 

©1995-2019 Prestwood IT Solutions.   [Security & Privacy]