7

我想在用户按下键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;

我尝试对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 结构的指针(如果系统正在执行查询,则为 NULL)。

wParamwParam都为零。

更新二

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

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.ResultDLGC_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 标志

所以这个比较好。光标键继续在编辑控件中导航文本(而不是转移焦点),并且我收到OnKeyPress(和OnKeyDownOnKeyUpTab键的事件。

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

奖金喋喋不休

4

2 回答 2

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;
于 2013-07-23T18:21:04.653 回答
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;
于 2013-07-23T19:44:57.117 回答