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

时间:2013-07-23 16:09:29

标签: delphi accessibility subclassing delphi-5

我想在用户按 Tab 键时收到OnKeyPress个事件。

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

我尝试子类化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;

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

正确接收标签键事件的方法是什么?

奖金阅读

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

  

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

     

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

wParamwParam均为零。

更新两个

我意识到我有相同的bug as this answer

if Message.Msg = WM_GETDLGCODE then
   Message.Result:= Message.Result or DLGC_WANTTAB
else
   if Assigned(FOldWndProc) then FOldWndProc(Message);

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

if Assigned(FOldWndProc) then FOldWndProc(Message);
if Message.Msg = WM_GETDLGCODE then
   Message.Result:= Message.Result or DLGC_WANTTAB;

这有助于解释我的原始代码错误的原因。将Message.Result设置为DLGC_WANTTAB是错误的:

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

尝试将bitwise or标记DLGC_WANTTAB导入Message.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;

我必须先调用原始窗口过程,才能让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;

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

  

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

所以这更好。光标键继续导航编辑控件中的文本(而不是移动焦点),我收到选项卡的<{1}}(以及OnKeyPressOnKeyDown)事件键。

剩下的问题是用户按 Tab 不再改变焦点。

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

OnKeyUp

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

  

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

就我而言,我打破了 Shift + Tab 。谁知道还有什么。


所以,我的问题:

  

如何在编辑框中按TAB键?

我不想他们,我只想知道用户按下 Tab 键。

Bonus Chatter

2 个答案:

答案 0 :(得分: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;


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

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

  inherited;
end;

答案 1 :(得分:3)

您需要先调用前一个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;