i want to receive OnKeyPress
events when the user presses the Tab key.
procedure TForm1.Edit1(Sender: TObject; var Key: Char);
begin
case Key of
#09:
begin
//Snip - Stuff i want to do
end;
end;
end;
i try subclassing the Edit
box, and handle the WM_GETDLGCODE
message:
procedure TfrmEnableVIPMode.AccountNumberWindowProc(var Message: TMessage);
begin
case Message.Msg of
WM_GETDLGCODE: Message.Result := DLGC_WANTTAB;
else
FOldAccountNumberWindowProc(Message);
end;
end;
And i now receive Tab KeyPress events (as i hoped), but now pressing the Left or Right cursor keys causes focus to shift to the previous, or next, control in the tab order.
What is the correct way to recieve Tab Key Press events?
i tried doing what the MSDN documentation says:
wParam
The virtual key, pressed by the user, that prompted Windows to issue this notification. The handler must selectively handle these keys. For instance, the handler might accept and process VK_RETURN but delegate VK_TAB to the owner window. For a list of values, see Virtual-Key Codes.lParam A pointer to an MSG structure (or NULL if the system is performing a query).
but wParam
and wParam
are both zero.
i realized i have the same bug as this answer:
if Message.Msg = WM_GETDLGCODE then
Message.Result:= Message.Result or DLGC_WANTTAB
else
if Assigned(FOldWndProc) then FOldWndProc(Message);
when i should actually use concepts from the correct code listed elsewhere in the same answer:
if Assigned(FOldWndProc) then FOldWndProc(Message);
if Message.Msg = WM_GETDLGCODE then
Message.Result:= Message.Result or DLGC_WANTTAB;
That helps to explain why my original code is wrong. Setting Message.Result
to DLGC_WANTTAB
is wrong:
procedure TfrmEnableVIPMode.AccountNumberWindowProc(var Message: TMessage);
begin
case Message.Msg of
WM_GETDLGCODE: Message.Result := DLGC_WANTTAB;
else
FOldAccountNumberWindowProc(Message);
end;
end;
it is also wrong to try to bitwise or
the flag DLGC_WANTTAB
into Message.Result
, because Message.Result
doesn't have a value yet:
procedure TfrmEnableVIPMode.AccountNumberWindowProc(var Message: TMessage);
begin
case Message.Msg of
WM_GETDLGCODE: Message.Result := Message.Result or DLGC_WANTTAB;
else
FOldAccountNumberWindowProc(Message);
end;
end;
i must first call the original window procedure, to get Windows' EDIT
control set correct values of Message.Result
. Then i can bitwise combine DLGC_WANTTAB
:
procedure TfrmEnableVIPMode.AccountNumberWindowProc(var Message: TMessage);
begin
FOldAccountNumberWindowProc(Message);
case Message.Msg of
WM_GETDLGCODE: Message.Result := Message.Result or DLGC_WANTTAB;
end;
end;
To paraphrase the Raymond Chen blog entry, and adding emphasis as required:
After asking the original control what behavior it thinks it wants, we turn on the DLGC_WANTTAB flag
So this is better. Cursor keys continue to navigate text in the Edit control (rather than shifting focus), and i receive OnKeyPress
(and OnKeyDown
and OnKeyUp
) events for the Tab key.
The remaining problem is that the user pressing Tab no longer shifts focus.
i tried to start to start manually hacking in focus changing myself:
procedure TfrmEnableVIPMode.edAccountNumberKeyPress(Sender: TObject; var Key: Char);
begin
case Key of
#09:
begin
//Snip - Stuff i want to do
{
The DLGC_WANTTAB technique broke Windows focus change.
Keep throwing in hacks until it's no longer obviously broken
}
//Perform(CM_DialogKey, VK_TAB, 0); //doesn't work
Self.ActiveControl := Self.FindNextControl(edAccountNumber, True, True, False);
end;
end;
end;
The above code works - if the user pressed the Tab key. But the code is broken, as Raymond Chen notes six year ago:
There are many things wrong with this approach. You can spend quite a lot of time nitpicking the little details, how this code fails to set focus in a dialog box properly, how it fails to take nested dialogs into account, how it fails to handle the Shift+Tab navigation key
In my case, i broke Shift+Tab. And who knows what else.
So, my question:
How to receive TAB key press in edit box?
i don't want to eat them, i just want to know that the user pressed the Tab key.
When you want to handle Tabkey for a TextBoxyou can simply handle PreviewKeyDownevent and surely you don't need to override ProcessCmdKeyand check what is the focused control! – Reza Aghaei Mar 10 '16 at 13:08 Cody Gray, I am sorry but I am not that versed in C# yet. I do not know how to: Subclass TextBox and override IsInputKey. – user2102327
CMS is suggesting using keydown because in IE, keypress does not work for noncharacter keys, (such as Tab) – Marc Aug 22 '09 at 0:58 5 @AppleGrew: it says so, yes, but it's not true - at least for keypress. For Tab e.which is 0 and e.keyCode is 9 (as it should be).
This method will allow a user to press the ENTER key and have the focus advance to the next edit control. If the focus is currently on the last edit control in the dialog box, the focus will advance to the first edit control.
As a common user-interface, however, one could also use the ENTER (RETURN) key to move between the edit controls (for example, after the user enters a piece of information, pressing ENTER moves the focus to the next field). There are a few ways to enable the use of the ENTER key to move between edit controls.
You can handle the CN_KEYDOWN
message:
procedure TfrmEnableVIPMode.AccountNumberWindowProc(var Message: TMessage);
begin
case Message.Msg of
CN_KEYDOWN:
if TWMKey(Message).CharCode = VK_TAB then
....
end;
FOldAccountNumberWindowProc(Message);
end;
It is also possible to detect the key down message at the form level, without subclassing the edit:
procedure TfrmEnableVIPMode.CMDialogKey(var Message: TCMDialogKey);
begin
if (Message.CharCode = VK_TAB) and (ActiveControl = edAccountNumber) then
...
inherited;
end;
You need to call the previous WndProc first so the Message.Result gets a default value for the key codes that TEdit
natively wants, then append your DLGC_WANTTAB
flag to that result, eg:
procedure TfrmEnableVIPMode.AccountNumberWindowProc(var Message: TMessage);
begin
FOldAccountNumberWindowProc(Message);
if Message.Msg = WM_GETDLGCODE then
Message.Result := Message.Result or DLGC_WANTTAB;
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With