Hi friends …
PRETEXTWhen I refer to activex controls I mean those which I test well, the richtextctrl, datetime picker and the month view.
(Other activex controls like forms2 textbox got other issues too, tested partially)Activex Keypress(), KeyDown(), KeyUp() Choas-------------------------------------------------------------------------
While I was developing a form in which I decided to use F5 function key as a shortcut key to refresh the form, as usual I set the keypreview property of the form to .T. to trap the key pressed and take an action … I placed a simple code to deal with the F5 key press.
* The keypress() of the form
LPARAMETERS nKeyCode, nShiftAltCtrl
WITH THIS
DO CASE
CASE nKeyCode=-4 && F5 .. Refresh
.myRefreshMethod()
NODEFAULT
CASE nKeyCode= ............
*
* ... etc
*
ENDCASE
ENDWITH
Till now ok …
But what I noticed is that when the focus is in a DateTimePicker on the form, the form will not be refreshed when I press F5.
This weird behavior lead me to test more … I found that:
- The keypress of the form was not receiving the key code … i.e. the keypreview setting is useless and the activex disrespect it and never passed the key to the form keypress.
- The keypress() of the activex contains this native code:
*** ActiveX Control Event ***
LPARAMETERS keyasciiThe twisted thing here is that … the key code passed to the keypress() of the activex is based on the
ASCII Character Codes Charts (see below) … which gives some similar key codes to that we depend on (that is listed in the table of key codes in the inkey() section of vfp help) like the number keys codes and basic English letters codes …
But .. at the same time … keypress() of the activex didn't received the Function keys pressed …. !! because these are not included in the basic
ASCII Character Codes Charts …
The keypress() of the activex doesn't receive the modifier keys (SHIFT, CTRL, ALT) nor does it receive function keys .. but it receive some keys like the Enter key, the Backspace, the Space and the ESC key ..
And as you noticed there is no
nShiftAltCtrl parameter as that in the keypress() of the native vfp controls, so the keypress() of the activex will not reflect the condition of the modifier keys …
This is because it is …..
ASCII code based … i.e. indicate which ASCII character was passed
whatever the key you pressed to invoke it … :-) …
- The activex has another two events to receive the key strokes …. :
KeyDown() and KeyUp() events. .. let's talk about these …..
- KeyDown (as the name is clear) is fired when a keyboard key is pressed down, and KeyUp will fire when it is released to spring up …
The native code in these 2 events is:
* the code in the KeyUp and KeyDown events
*** ActiveX Control Event ***
LPARAMETERS keycode, shift- The irony here is that .. this keycode here could have different values from that of the key codes in the keypress() of the activex and that of the native form …..
This is because … here, the
keycode reflects The
Physical Key pressed !! and there is another table for these key codes .. see below (so you get 3 tables, one for ASCII code, one for these physical key values, and one for the vfp native nkeycode value as shown in the inkey() function help document in vfp help)
So that keycode of the letter 'a' is the same as to its capital 'A'. The only difference is that
shift value will indicate whether modifier keys were pressed or not, the value as follows:
Shift value modifier key 1 SHIFT
2 CTRL
4 ALT
And this value is additive, that mean if we press SHIFT+CTRL, the value of shift is 3
But still in fact keyup() and keydown() in this case are not useful to detect which ASCII key was pressed because the value returned by shift is not related to what is the setting of the Caps key is !! …
In our example, if Caps key was ON and we press the keyboard key 'A' without SHIFT, we are passing 'A' letter to the keyup and keydown events, the
keycode value in keyup and keydown will be (65) according to the key codes table used by keyup and keydown, and
shift will be 0.
But Again, if Caps key was OFF and we press the keyboard key 'A' without SHIFT, we are passing 'a' letter to the keyup and keydown events, the
keycode value in keyup and keydown will also be (65) according to the key codes table used by keyup and keydown, and
shift will also be 0. !! … So, in this case we need to know the condition of the Caps key to know what's going on ..
On the other hand, when we press the key '0', its key ASCII in the keypress event is (48) whether we pressed the zero in the main keyboard numbers or in the numpad. While in the keyup and keydown, the keycode is (48) if the 0 key pressed in the main keyboard and it is (96) if the numpad 0 was pressed.
- Therefore, MSDN .. recommendation is:
" Although the KeyDown and KeyUp events can apply to most keys, they're most often used for:
- Extended character keys such as function keys.
- Navigation keys.
- Combinations of keys with standard keyboard modifiers.
- Distinguishing between the numeric keypad and regular number keys.
"- Note here that … it is a native behavior of the keyboard to send repeated keys when you keep pressing down the key until you move your finger up. …. Like this if I keep pressing the 'a' key :
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
In this case .. KeyDown will be fired with each one and KeyUp will be fired at the end only …
Another (but) … If the key you pressed is processed by keypress() of the activex (if it is an ASCII character), then the keypress() of the activex will be fired also :-) .. and the sequence of events will be like this for one rapid key press:
KeyDown ---
> KeyPress ---
> KeyUp
But for a press with a hold down say for 3 repeats, the event sequence will be like this:
KeyDown ---
> KeyPress ---
> KeyDown ---
> KeyPress ---
> KeyDown ---
> KeyPress ---
> KeyUp
A third (but) ….. if the key you are holding is not processed by the keypress (because it is not an ASCII character), like the SHIFT key or the CTRL key, the sequence of events will be:
KeyDown ---
> KeyDown ---
> KeyDown ---
> KeyUp
This is important to be noticed if you want to put a code in these events since these will fire repeatedly .. especially the keydown() event that I thought it will fire once as do the keyup() event, but is not
- If we have a command button with its Default property is set to .T. or a command button with its Cancel Property set to .T., it is supposed that the activex control respects these settings (as the native vfp controls does) but … it doesn't !!
It doesn't … although in the MSDN documentation of the KeyUp and KeyDown events it says that pressing ENTER key will not invoke these events if there is a commandbutton on the form with its Default property set to .T. … and pressing ESC key will not invoke these events if there is a commandbutton on the form with its Cancel property set to .T. ………
This is not happening here in visual foxpro .. may be it is true in Visual Basic only !! I don't know.
- AND .. in MSDN, it is said that TAB key will NOT invoke the keyup and keydown events … but … by testing I found that:
If we press the TAB key inside the activex .. we have different responses:
- If there is another control that will receive the focus after the activex, then the event sequence will be as follows:
KeyDown() of the activex ---
> KeyPress() of the parent Form. ---
> lostfocus() of the activex !!!
That's all .. no keypress() of the activex is fired (The ASCII tab chr(9) fires it which can be produced by CTRL+I which inserts a tab space, like in the text part of the richtextctrl, not used to jump from a control to another) … And .. No keyUp() event.
- If we used TAB to move from a control on the form to the activex then the sequence of events will be:
Gotfocus() of the activex ---
> KeyUP() … And no KeyDown() event.
- if there is no other active control that can receive the focus, then the sequence of events will be like this:
KeyDown() ---
> KeyPress() of the Form ---
> lostfocus() of the activex ---
> gotfocus() of the activex ---
> KeyUp()
If we press just a modifier key once, lets take SHIFT, inside the activex … the keypress() will not fire (of course .. not an ASCII character)
The KeyDown() will be fired first with these values:
Keycode=16 …. Shift=1Then KeyUp() will be fired with these settings:
Keycode=16 …. Shift=0In the keydown event, Its like saying … "
you pressed the SHIFT key + SHIFT key"
And In the keydUp event, Its like saying …"
you pressed the SHIFT key without the SHIFT key"
Funny and ironic :-)
- So, in foxpro .. activex key events are chaotic … when we look at it with the usual keypress event behavior we used to in vfp controls.
Now to the struggle to overcome these puzzles ….CASE No 1:What I was trying to do in my form (as I said above) is that I want to use F5 key to call a custom refresh method in my form ….
The code I used in the keypress() event of the form is clear … (see it at the beginning of this article). And it works fine inside the form as far as I am away from the datetime picker .. so now how can we overcome that and let the F5 key works even in the activex control. ??
Several options I tried:
1) Using
ON KEY LABEL:The use of ON KEY LABEL need careful control so that it will not interrupt a critical running code. So, we may use it within the confines of a modal form. Defined in the init event and set to its default in the destroy event like this:
* init event of the formON KEY LABEL F5 EXECSCRIPT("_vfp.activeform.myRefresh")
* Destroy event of the form
ON KEY LABEL F5And the code I used in the keypress() event of the form to trap the F5 will be discarded. The code in myRefresh will be excuted directly with the F5 press.
OR:
I keep my code in the keypress() of the form and Just use ON KEY LABEL within the confines of the activex itself like this:
* GotFocus event of the activex
ON KEY LABEL F5 EXECSCRIPT("_vfp.activeform.myRefresh")
* LostFocus event of the activex
ON KEY LABEL F52) Using
BINDEVENT():
I was expecting that using BindEvent() will solve the problem .. by trapping the keypress() event of the activex or even the keyUp() or KeyDown() events in a code like this:
* init event of the form
BINDEVENT(THISFORM.oleMyActivex, "keypress", thisform,;
"keypress")Here … whether binding keypress, keydown, keyup .. of the activex to the keypress of the form (and whatever the flag is and whatever the type of keycode is) .. IT DOESN"T BIND … It bind it to somewhere?!!
It looks like the activex is behaving like a standalone object … not respecting KeyPreview nor respecting any BindEvent !! …
So, I said .. may be I should regard it as a Window .. I tried this code to bind it using hWnd and windows message codes like this:
* init event of the form
#define WM_KEYUP 0x0101
BINDEVENT(thisform.oleMyActivex.hWnd,WM_KEYUP, thisform, "keypress")It doesn"t bind too !! :-(
Notes:- The activex hWnd property is not seen in the PEM window but seen with IntelliSense!!
- I took the windows message value (
WM_KEYUP 0x0101) from wmhandler.h available for download in MSDN .. and I uploaded it
here (change the extension to .h).
- I am not sure this last binding is logical or not, but it seems logical to me ..not sure!.
So, I failed with this method.
3) Passing the keypress() or keyUp() event key codes and shift values into keypress() of the form like this:
Use keyUp() to trap the function keys as far as these don't fire the keypress() of the activex.
* KeyUp() of the activex
*** ActiveX Control Event ***
LPARAMETERS keycode, SHIFT
DO CASE
CASE keycode=116 && F5
THISFORM.KEYPRESS(-4,SHIFT)
NODEFAULT
ENDCASEHere the keycode need to be received as 116 and passed to the keypress() of the form as -4 … !! The tables that help us deal with these differences in keycodes are shown at the end of the article.
Or we can send the keycode "as-is" and translate it in the form keypress() event.
CASE No. 2If we like to use a richtextctrl control and want to work with it using the conventional shortcuts like: CTRL+B =
bold, CTRL+I =
italic, CTRL+U =
underline. So that just selecting some text in the richtext control and pressing these shortcuts will format it accordingly.
The code I used to control this shortcut function is:
* KeyUp() of the richtextctrl
WITH THIS
DO CASE
CASE keycode=66 AND SHIFT=2 && CTRL+B
.selBold=!.selBold
NODEFAULT
CASE keycode=73 AND SHIFT=2 && CTRL+I
.selitalic=!.selitalic
NODEFAULT
CASE keycode=85 AND SHIFT=2 && CTRL+U
.selUnderline=!.selUnderline
NODEFAULT
ENDCASE
ENDWITHI faced some problems here ….
1) If we run the form that contains the activex within the main vfp window (in-screen or in-top-level form), CTRL+B will call the Breakpoints window.
2) CTRL+I in the keypress event of the activex is interpreted as a tab space, chr(9) which is the standard ASCII shortcut.
Progress:Running the form As top level form .. solved the problem of Breakpoints window. And the shortcut CTRL+B works fine.
CTRL+U is already working fine.
CTRL+I continued to produce tab spaces despite the assignment in the code above and the NODEFAULT command.!!
Even putting the code in the keydown() event that fires before the keypress didn't change this behavior.
So, what I did is I cancelled the original assignment of CTRL+I within the activex control and return it back when it loses the focus like this:
* GotFocus event of the activex
ON KEY LABEL CTRL+I *
* LostFocus event of the activex
ON KEY LABEL CTRL+IWhen I use this approach, the code above that I put in the keyup() event will work fine whether put in the keyup() or keydown() event.
But CTRL+I will Not work if I used the code in the keypress() event of the activex like this:
* keyup() of the richtextctrl
WITH THIS
DO CASE
CASE keyascii=2 && ^B
.selBold=!.selBold
NODEFAULT
CASE keyascii=9 && ^I
.selitalic=!.selitalic
NODEFAULT
CASE keyascii=21 && ^U
.selUnderline=!.selUnderline
NODEFAULT
ENDCASE
ENDWITHThis is because the keypress() event will not receive CTRL+I anymore since it is no more an ASCII character. But received by keyup() and keydown() as far as they represent a physical key combination as I explained above.
Note:We can Add CTRL+B to the disabling code if we want to run the form in the main vfp window so the code will be:
* GotFocus event of the activex
ON KEY LABEL CTRL+I *
ON KEY LABEL CTRL+B *
* LostFocus event of the activex
ON KEY LABEL CTRL+I
ON KEY LABEL CTRL+BNow …. The key code tables ..1) nKeyCode values of the native visual foxpro controls are found in vfp help in the INKEY() section.
2) ASCII character codes chart 1 (0-127) (used in keypress event of the activex):
http://msdn.microsoft.com/en-us/library/60ecse8t(VS.71).aspx
The picture below is to show the description of the codes from 0-31

3) ASCII character codes chart 2 (128-255) (used in keypress event of the activex):
http://msdn.microsoft.com/en-us/library/9hxt0028(VS.71).aspx"The characters that appear in Windows above 127 depend on the selected typeface."
4) Key Code Constants for keyup() and keydown():
http://msdn.microsoft.com/en-us/library/aa243025(VS.60).aspxKey Codes
|
Constant
|
Value
|
Description
|
|
vbKeyLButton
|
1
|
Left mouse button
|
|
vbKeyRButton
|
2
|
Right mouse button
|
|
vbKeyCancel
|
3
|
CANCEL key
|
|
vbKeyMButton
|
4
|
Middle mouse button
|
|
vbKeyBack
|
8
|
BACKSPACE key
|
|
vbKeyTab
|
9
|
TAB key
|
|
vbKeyClear
|
12
|
CLEAR key
|
|
vbKeyReturn
|
13
|
ENTER key
|
|
vbKeyShift
|
16
|
SHIFT key
|
|
vbKeyControl
|
17
|
CTRL key
|
|
vbKeyMenu
|
18
|
MENU key
|
|
vbKeyPause
|
19
|
PAUSE key
|
|
vbKeyCapital
|
20
|
CAPS LOCK key
|
|
vbKeyEscape
|
27
|
ESC key
|
|
vbKeySpace
|
32
|
SPACEBAR key
|
|
vbKeyPageUp
|
33
|
PAGE UP key
|
|
vbKeyPageDown
|
34
|
PAGE DOWN key
|
|
vbKeyEnd
|
35
|
END key
|
|
vbKeyHome
|
36
|
HOME key
|
|
vbKeyLeft
|
37
|
LEFT ARROW key
|
|
vbKeyUp
|
38
|
UP ARROW key
|
|
vbKeyRight
|
39
|
RIGHT ARROW key
|
|
vbKeyDown
|
40
|
DOWN ARROW key
|
|
vbKeySelect
|
41
|
SELECT key
|
|
vbKeyPrint
|
42
|
PRINT SCREEN key
|
|
vbKeyExecute
|
43
|
EXECUTE key
|
|
vbKeySnapshot
|
44
|
SNAPSHOT key
|
|
vbKeyInsert
|
45
|
INS key
|
|
vbKeyDelete
|
46
|
DEL key
|
|
vbKeyHelp
|
47
|
HELP key
|
|
vbKeyNumlock
|
144
|
NUM LOCK key
|
KeyA Through KeyZ Are the Same as Their ASCII Equivalents: 'A'
Through 'Z'
|
Constant
|
Value
|
Description
|
|
vbKeyA
|
65
|
A key
|
|
vbKeyB
|
66
|
B key
|
|
vbKeyC
|
67
|
C key
|
|
vbKeyD
|
68
|
D key
|
|
vbKeyE
|
69
|
E key
|
|
vbKeyF
|
70
|
F key
|
|
vbKeyG
|
71
|
G key
|
|
vbKeyH
|
72
|
H key
|
|
vbKeyI
|
73
|
I key
|
|
vbKeyJ
|
74
|
J key
|
|
vbKeyK
|
75
|
K key
|
|
vbKeyL
|
76
|
L key
|
|
vbKeyM
|
77
|
M key
|
|
vbKeyN
|
78
|
N key
|
|
vbKeyO
|
79
|
O key
|
|
vbKeyP
|
80
|
P key
|
|
vbKeyQ
|
81
|
Q key
|
|
vbKeyR
|
82
|
R key
|
|
vbKeyS
|
83
|
S key
|
|
vbKeyT
|
84
|
T key
|
|
vbKeyU
|
85
|
U key
|
|
vbKeyV
|
86
|
V key
|
|
vbKeyW
|
87
|
W key
|
|
vbKeyX
|
88
|
X key
|
|
vbKeyY
|
89
|
Y key
|
|
vbKeyZ
|
90
|
Z key
|
Key0 Through Key9 Are the Same as Their ASCII Equivalents: '0'
Through '9
|
Constant
|
Value
|
Description
|
|
vbKey0
|
48
|
0 key
|
|
vbKey1
|
49
|
1 key
|
|
vbKey2
|
50
|
2 key
|
|
vbKey3
|
51
|
3 key
|
|
vbKey4
|
52
|
4 key
|
|
vbKey5
|
53
|
5 key
|
|
vbKey6
|
54
|
6 key
|
|
vbKey7
|
55
|
7 key
|
|
vbKey8
|
56
|
8 key
|
|
vbKey9
|
57
|
9 key
|
Keys on the Numeric Keypad
|
Constant
|
Value
|
Description
|
|
vbKeyNumpad0
|
96
|
0 key
|
|
vbKeyNumpad1
|
97
|
1 key
|
|
vbKeyNumpad2
|
98
|
2 key
|
|
vbKeyNumpad3
|
99
|
3 key
|
|
vbKeyNumpad4
|
100
|
4 key
|
|
vbKeyNumpad5
|
101
|
5 key
|
|
vbKeyNumpad6
|
102
|
6 key
|
|
vbKeyNumpad7
|
103
|
7 key
|
|
vbKeyNumpad8
|
104
|
8 key
|
|
vbKeyNumpad9
|
105
|
9 key
|
|
vbKeyMultiply
|
106
|
MULTIPLICATION SIGN (*) key
|
|
vbKeyAdd
|
107
|
PLUS SIGN (+) key
|
|
vbKeySeparator
|
108
|
ENTER (keypad) key
|
|
vbKeySubtract
|
109
|
MINUS SIGN (-) key
|
|
vbKeyDecimal
|
110
|
DECIMAL POINT(.) key
|
|
vbKeyDivide
|
111
|
DIVISION SIGN (/) key
|
Function Keys
|
Constant
|
Value
|
Description
|
|
vbKeyF1
|
112
|
F1 key
|
|
vbKeyF2
|
113
|
F2 key
|
|
vbKeyF3
|
114
|
F3 key
|
|
vbKeyF4
|
115
|
F4 key
|
|
vbKeyF5
|
116
|
F5 key
|
|
vbKeyF6
|
117
|
F6 key
|
|
vbKeyF7
|
118
|
F7 key
|
|
vbKeyF8
|
119
|
F8 key
|
|
vbKeyF9
|
120
|
F9 key
|
|
vbKeyF10
|
121
|
F10 key
|
|
vbKeyF11
|
122
|
F11 key
|
|
vbKeyF12
|
123
|
F12 key
|
|
vbKeyF13
|
124
|
F13 key
|
|
vbKeyF14
|
125
|
F14 key
|
|
vbKeyF15
|
126
|
F15 key
|
|
vbKeyF16
|
127
|
F16 key |
Download links:- A sample form to show the use of shortcuts, print with CTRL-P, format a richtext with CTRL+B, CTRL+I, CTRL+U. here
- wmhandler.h (windows messaging handler h file): here (change its extension to .h)
Some resources:-
Ammar Hadi ................ Iraq