You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi 10.2+实现子窗体模态化及Tab键仅在子窗体循环

Delphi 10.2及以上版本实现嵌入子窗体的类模态效果

需求与问题描述

需要实现嵌入在主窗体(红色)中的子窗体(绿色)的类模态效果:

  • 子窗体获取焦点后,Tab键导航仅限制在自身控件范围内循环
  • 禁止焦点跳转到父窗体,同时不能使用ShowModal(不符合嵌入需求)或禁用整个主窗体的方式(会导致整个应用无法交互)

当前问题:子窗体包含多个可Tab导航的控件,但Tab到最后一个控件后再按Tab,焦点会跳转到父窗体控件,无法在子窗体内循环。

子窗体运行时创建代码:

GreenWindow := TForm.Create(Application);
GreenWindow.Parent := Application.MainForm;
...
//Application.MainForm.Enabled := False; // 此代码会阻止整个应用与用户交互
GreenWindow.Show; // ShowModal完全不符合需求

尝试的解决方案

方案1:子窗体OnKeyDown事件拦截Tab键

在子窗体的OnKeyDown事件中捕获Tab键,手动调用FindNextControl查找下一个/上一个可聚焦控件,阻止默认Tab行为:

procedure TChildForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var
  FocusedControl: TWinControl;
  NextControl: TWinControl;
begin
  if Key = VK_TAB then begin
    FocusedControl := ActiveControl;
    if FocusedControl <> Nil then begin
      NextControl := TWinControlAccess(Self).FindNextControl(FocusedControl, not (ssShift in Shift), True, True);

      if Assigned(NextControl) then
        NextControl.SetFocus;

      Key := 0; // 阻止默认处理
    end;
  end;
end;

方案2:父窗体开启KeyPreview并控制焦点

将父窗体的KeyPreview属性设为True,在OnKeyDown事件中检查焦点控件是否属于子窗体,仅允许焦点在子窗体内切换:

procedure TParentForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var
  FocusedControl: TWinControl;
  NextControl: TWinControl;
begin
  if Key = VK_TAB then begin
    FocusedControl := ActiveControl;
    if FocusedControl <> Nil then begin
      NextControl := TWinControlAccess(Self).FindNextControl(FocusedControl, not (ssShift in Shift), True, True);

      if Assigned(NextControl) and ((NextControl = FModalForm) or (GetParentForm(NextControl.Parent) = FModalForm)) then
        NextControl.SetFocus;

      Key := 0; // 阻止默认处理
    end;
  end;
end;

方案3:禁用父窗体及外部控件的TabStop

将父窗体和子窗体以外的所有控件的TabStop属性设为False,阻止它们获得Tab焦点,但此方法需要手动遍历控件,扩展性较差。

最终可行方案:拦截父窗体CM_DIALOGKEY消息

Delphi默认会处理Tab键的对话框导航逻辑,导致父窗体OnKeyDown无法捕获Tab键。需先拦截CM_DIALOGKEY消息,让OnKeyDown事件正常触发,进而完全控制Tab导航:

首先在父窗体类中声明消息处理方法:

procedure CMDialogKey(var Message: TCMDialogKey); message CM_DIALOGKEY;

然后实现消息处理逻辑:

procedure TParentForm.CMDialogKey(var Message: TCMDialogKey);
begin
  if GetKeyState(VK_MENU) >= 0 then begin
    if Message.CharCode = VK_TAB then 
      Exit;
  end;
  inherited;
end;

拦截后,父窗体的OnKeyDown事件会触发,即可按照方案2的逻辑实现Tab焦点仅在子窗体内循环。


内容的提问来源于stack exchange,提问作者NevTon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 23:37:05