【问题标题】:How to receive TAB key press in edit box?如何在编辑框中接收 TAB 键按下?
【发布时间】:2013-07-22 20:00:18
【问题描述】:

我想在用户按下 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 事件(如我所愿),但现在按 LeftRight 光标键使焦点转移到 Tab 键顺序中的上一个或下一个控件。

接收 Tab Key Press 事件的正确方式是什么?

阅读奖励

我尝试按照 MSDN 文档中的说明进行操作:

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

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

但是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 标志

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

剩下的问题是用户按下 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;

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

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

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


所以,我的问题:

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

我不想吃掉它们,我只想知道用户按下了Tab键。

奖金聊天

【问题讨论】:

  • 您可能会在this post获得灵感。
  • @TLama 它确实帮助我找到了我正在使用的方法中的一个(常见)错误。我可能使用了完全错误的方法,但至少我修复了错误的错误方法!
  • 首先您说“我希望编辑控件获取 TAB 键而不是移动焦点”,然后您抱怨“编辑控件获取 TAB 键而不是移动焦点!”你要哪个?
  • +1 详尽,但值得通读。
  • @RaymondChen 这些是 Delphi 表格;不是实际的 Windows “对话框”。也许这与意外行为有关。

标签: delphi accessibility subclassing delphi-5


【解决方案1】:

您可以处理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;

【讨论】:

    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2012-01-06
      • 1970-01-01
      • 2016-03-06
      • 1970-01-01
      • 2012-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多