如何在编辑框中接收TAB键?

Ian*_*oyd 7 delphi accessibility delphi-5 subclassing

我想OnKeyPress在用户按下Tab键时接收事件.

procedure TForm1.Edit1(Sender: TObject; var Key: Char);
begin
   case Key of
   #09:
      begin
         //Snip - Stuff i want to do
      end;
   end;
end;
Run Code Online (Sandbox Code Playgroud)

我尝试子类化Edit框,并处理WM_GETDLGCODE消息:

procedure TfrmEnableVIPMode.AccountNumberWindowProc(var Message: TMessage);
begin
   case Message.Msg of
   WM_GETDLGCODE: Message.Result := DLGC_WANTTAB;
   else
      FOldAccountNumberWindowProc(Message);
   end;
end;
Run Code Online (Sandbox Code Playgroud)

我现在收到Tab KeyPress事件(正如我所希望的那样),但现在按下LeftRight光标键会使焦点转移到Tab键顺序中的上一个或下一个控件.

收到Tab键按键事件的正确方法是什么?

奖金阅读

我试着做MSDN文档所说的内容:

wParam
用户按下的虚拟键,提示Windows发出此通知.处理程序必须有选择地处理这些键.例如,处理程序可能接受并处理VK_RETURN,但将VK_TAB委托给所有者窗口.有关值列表,请参阅虚拟键代码.

lParam指向MSG结构的指针(如果系统正在执行查询,则为NULL).

wParamwParam均为零.

更新二

我意识到我和这个答案有同样的错误:

if Message.Msg = WM_GETDLGCODE then
   Message.Result:= Message.Result or DLGC_WANTTAB
else
   if Assigned(FOldWndProc) then FOldWndProc(Message);
Run Code Online (Sandbox Code Playgroud)

当我真的应该使用同一答案中其他地方列出的正确代码中的概念:

if Assigned(FOldWndProc) then FOldWndProc(Message);
if Message.Msg = WM_GETDLGCODE then
   Message.Result:= Message.Result or DLGC_WANTTAB;
Run Code Online (Sandbox Code Playgroud)

这有助于解释为什么我的原始代码是错误的.设置Message.ResultDLGC_WANTTAB是错误的:

procedure TfrmEnableVIPMode.AccountNumberWindowProc(var Message: TMessage);
begin
   case Message.Msg of
   WM_GETDLGCODE: Message.Result := DLGC_WANTTAB;
   else
      FOldAccountNumberWindowProc(Message);
   end;
end;
Run Code Online (Sandbox Code Playgroud)

这也是错误的尝试bitwise orDLGC_WANTTABMessage.Result,因为Message.Result没有一个价值尚未:

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;
Run Code Online (Sandbox Code Playgroud)

我必须首先调用原始窗口过程,以使Windows的EDIT控件设置正确的值Message.Result.然后我可以按位组合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;
Run Code Online (Sandbox Code Playgroud)

要解释Raymond Chen的博客文章,并根据需要增加重点:

在询问原始控件它认为它想要的行为之后,我们打开DLGC_WANTTAB标志

所以这更好.光标键继续导航编辑控件中的文本(而不是移动焦点),我接收OnKeyPress(OnKeyDownOnKeyUp)Tab键的事件.

剩下的问题是用户按下Tab不再移动焦点.

我试图开始手动黑客攻击改变自己:

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;
Run Code Online (Sandbox Code Playgroud)

上面的代码有效 - 如果用户按下了Tab键.但正如Raymond Chen六年前所说的那样,代码被破坏了:

这种方法有很多问题.您可以花费大量时间来挑剔细节,这些代码如何无法正确地在对话框中设置焦点,如何将嵌套对话框考虑在内,如何无法处理Shift + Tab导航键

在我的情况下,我打破Shift+ Tab.谁知道还有什么.


所以,我的问题:

如何在编辑框中接收TAB键?

我不想它们,我只是想知道用户按下了Tab钥匙.

奖金Chatter

Ser*_*yuz 7

您可以处理以下CN_KEYDOWN消息:

procedure TfrmEnableVIPMode.AccountNumberWindowProc(var Message: TMessage);
begin
   case Message.Msg of
   CN_KEYDOWN:
      if TWMKey(Message).CharCode = VK_TAB then
         ....
   end;
   FOldAccountNumberWindowProc(Message);
end;
Run Code Online (Sandbox Code Playgroud)


还可以在表单级别检测密钥消息,而无需对编辑进行子类化:

procedure TfrmEnableVIPMode.CMDialogKey(var Message: TCMDialogKey);
begin
  if (Message.CharCode = VK_TAB) and (ActiveControl = edAccountNumber) then
    ...

  inherited;
end;
Run Code Online (Sandbox Code Playgroud)


Rem*_*eau 5

您需要先调用之前的 WndProc,以便 Message.Result 获得TEdit本机需要的键代码的默认值,然后将您的DLGC_WANTTAB标志附加到该结果,例如:

procedure TfrmEnableVIPMode.AccountNumberWindowProc(var Message: TMessage);
begin
  FOldAccountNumberWindowProc(Message);
  if Message.Msg = WM_GETDLGCODE then
    Message.Result := Message.Result or DLGC_WANTTAB;
end;
Run Code Online (Sandbox Code Playgroud)