Author: Ernesto De Spirito
We can migrate old DOS applications to the Windows environment, but frequently we
can't migrate the users :)
Answer:
We capture the keyboard messages with the OnMessage event of the Application
object. You can find similar articles, but the code presented here is more complete
and takes into account certain special cases.
For the ENTER key (VK_RETURN) we want to move to the next control in the case of
edit boxes and other controls, so we ask if the active control descends from
TCustomEdit, which includes TEdit, TDBEdit, TMaskEdit, TDBMaskEdit, TMemo, TDBMemo
and other components provided by third parties. Since we want to exclude TMemo,
TDBMemo and, in general, all descendants of TCustomMemo, we make a special proviso
in this case (leaving the message unchanged with no action), leaving us with the
single-line edit controls, to which we add listboxes, comboxes, etc. For these
elements we replace the ENTER key (VK_RETURN) by a TAB key (VK_TAB), both for the
WM_KEYDOWN and WM_KEYUP events.
However in the case of a combobox (any TCustomCombobox descendant), when the list
is dropped down we wish to maintain the traditional behaviour of the ENTER key
(i.e. closing the list).
It would be nice to have a keyboard shortcut for the default button of a form (the
button with its Default property set to True), for example CTRL+ENTER. This feature
is included in the code. The way it is accomplished is a little bit complex to
explain... Perhaps it would have been easier to iterate thru the components on a
form to find a focuseable button with Default = True, and then call its Click
method, but we used a code similar to the one used in VCL forms, which takes into
account the fact that the ENTER key might be wanted to get trapped by many
controls, not only a button.
We also want the DOWN arrow key (VK_DOWN) to be mapped as a TAB key (VK_TAB). For
this case we used a simpler code. Of course, we also want the UP arrow key (VK_UP)
to be mapped to a SHIFT+TAB key combination. Well, it isn't possible to map a key
with a modifier. We can descard the key and simulate the events of pressing SHIFT
and then TAB, or we can change the state of the SHIFT key in the keyboard state
array (like we did with the CTRL key in the CTRL+ENTER combination), but we took a
different approach (simply focusing the previous control of the active control in
the tab order).
Finally, for Spanish applications, it is usually desirable to replace the decimal
point of the numeric keypad with a coma (decimal separator in Spanish).
Well, enough talking, and here's the code:
1 type
2 TForm1 = class(TForm)
3 ...
4 private
5 ...
6 procedure ApplicationMessage(var Msg: TMsg; var Handled: Boolean);
7 ...
8 end;
9
10 var
11 Form1: TForm1;
12
13 implementation
14
15 {$R *.DFM}
16
17 procedure TForm1.FormCreate(Sender: TObject);
18 begin
19 Application.OnMessage := ApplicationMessage;
20 end;
21
22 procedure TForm1.ApplicationMessage(var Msg: TMsg;
23 var Handled: Boolean);
24 var
25 ActiveControl: TWinControl;
26 Form: TCustomForm;
27 ShiftState: TShiftState;
28 KeyState: TKeyboardState;
29 begin
30 case Msg.message of
31 WM_KEYDOWN, WM_KEYUP:
32 case Msg.wParam of
33 VK_RETURN:
34 // Replaces ENTER with TAB, and CTRL+ENTER with ENTER...
35 begin
36 GetKeyboardState(KeyState);
37 ShiftState := KeyboardStateToShiftState(KeyState);
38 if (ShiftState = []) or (ShiftState = [ssCtrl]) then
39 begin
40 ActiveControl := Screen.ActiveControl;
41 if (ActiveControl is TCustomComboBox) and
42 (TCustomComboBox(ActiveControl).DroppedDown) then
43 begin
44 if ShiftState = [ssCtrl] then
45 begin
46 KeyState[VK_LCONTROL] := KeyState[VK_LCONTROL] and $7F;
47 KeyState[VK_RCONTROL] := KeyState[VK_RCONTROL] and $7F;
48 KeyState[VK_CONTROL] := KeyState[VK_CONTROL] and $7F;
49 SetKeyboardState(KeyState);
50 end;
51 end
52 else if (ActiveControl is TCustomEdit)
53 and not (ActiveControl is TCustomMemo)
54 or (ActiveControl is TCustomCheckbox)
55 or (ActiveControl is TRadioButton)
56 or (ActiveControl is TCustomListBox)
57 or (ActiveControl is TCustomComboBox)
58 {// You can add more controls to the list with "or" } then
59 if ShiftState = [] then
60 begin
61 Msg.wParam := VK_TAB
62 end
63 else
64 begin // ShiftState = [ssCtrl]
65 Msg.wParam := 0; // Discard the key
66 if Msg.message = WM_KEYDOWN then
67 begin
68 Form := GetParentForm(ActiveControl);
69 if (Form <> nil) and
70 (ActiveControl.Perform(CM_WANTSPECIALKEY,
71 VK_RETURN, 0) = 0) and
72 (ActiveControl.Perform(WM_GETDLGCODE, 0, 0)
73 and DLGC_WANTALLKEYS = 0) then
74 begin
75 KeyState[VK_LCONTROL] := KeyState[VK_LCONTROL] and $7F;
76 KeyState[VK_RCONTROL] := KeyState[VK_RCONTROL] and $7F;
77 KeyState[VK_CONTROL] := KeyState[VK_CONTROL] and $7F;
78 SetKeyboardState(KeyState);
79 Form.Perform(CM_DIALOGKEY, VK_RETURN, Msg.lParam);
80 end;
81 end;
82 end;
83 end;
84 end;
85 VK_DOWN:
86 begin
87 GetKeyboardState(KeyState);
88 if KeyboardStateToShiftState(KeyState) = [] then
89 begin
90 ActiveControl := Screen.ActiveControl;
91 if (ActiveControl is TCustomEdit)
92 and not (ActiveControl is TCustomMemo)
93 {// You can add more controls to the list with "or" } then
94 Msg.wParam := VK_TAB;
95 end;
96 end;
97 VK_UP:
98 begin
99 GetKeyboardState(KeyState);
100 if KeyboardStateToShiftState(KeyState) = [] then
101 begin
102 ActiveControl := Screen.ActiveControl;
103 if (ActiveControl is TCustomEdit)
104 and not (ActiveControl is TCustomMemo)
105 {// You can add more controls to the list with "or" } then
106 begin
107 Msg.wParam := 0; // Discard the key
108 if Msg.message = WM_KEYDOWN then
109 begin
110 Form := GetParentForm(ActiveControl);
111 if Form <> nil then // Move to previous control
112 Form.Perform(WM_NEXTDLGCTL, 1, 0);
113 end;
114 end;
115 end;
116 end;
117 // Replace the decimal point of the numeric key pad (VK_DECIMAL)
118 // with a comma (key code = 188). For Spanish applications.
119 VK_DECIMAL:
120 begin
121 GetKeyboardState(KeyState);
122 if KeyboardStateToShiftState(KeyState) = [] then
123 begin
124 Msg.wParam := 188;
125 end;
126 end;
127 end;
128 end;
129 end;
Copyright (c) 2001 Ernesto De Spirito edspirito@latiumsoftware.com
Visit: http://www.latiumsoftware.com/delphi-newsletter.php
|