使表单内的表单在单击时成为焦点

Make forms inside a form catch focus when clicked

提问人:Codrut 提问时间:11/15/2023 更新时间:11/15/2023 访问量:80

问:

在 Delphi 中,您可以通过更改 TForm.Parent 属性在另一个窗体中创建一个 Form。

有两种方法可以在另一种形式中制作表单

正态 (fsNormal)

下面是在表单中创建表单的示例

  with TForm.Create(Self) do
    begin
      Parent := Self;
      Caption := 'Test 1';

      Width := 350;
      Height := 200;

      Show;
    end;

问题是他们在与他们互动时没有正确行动。捕获焦点只能通过单击标题栏或使用 Form.SetFocus 来工作,它们包含的控件也是如此。窗体将显示在窗体可能包含的任何 TWinControls 或 TGraphicControl 上方。

example of form inside form

MDI 表单 ('fsMDIChild')

执行此操作的正确方法是使用 MDI 表单。喜欢这个:

  Form.Style := fsMDIForm;

  with TForm.Create(Self) do
    begin
      Parent := Self;
      Caption := 'Test 1';

      Width := 350;
      Height := 200;

      FormStyle := fsMDIChild;
      Show;
    end;

  with TForm.Create(Self) do
    begin
      Parent := Self;
      Caption := 'Test 2';

      Width := 350;
      Height := 200;

      FormStyle := fsMDIChild;
      Show;
    end;

并且比形式会正常行动。喜欢这个:example of mdi forms working

问题

MDI 表单非常适合我的用例,如果不是针对 2 个问题:

  1. 边框,称为“ClientEdge”fsMDIForm
  2. 窗体内的控件显示在 MDI 窗体上方

第一个问题可以通过覆盖 轻松解决。过程是这样的:ClientWndProc

procedure ShowMDIClientEdge(ClientHandle: THandle; ShowEdge: Boolean);
var
  Style: NativeInt;
begin
  if ClientHandle <> 0 then
  begin
    Style := GetWindowLong(ClientHandle, GWL_EXSTYLE);
    if ShowEdge then
      if Style and WS_EX_CLIENTEDGE = 0 then
        Style := Style or WS_EX_CLIENTEDGE
      else
        Exit
    else if Style and WS_EX_CLIENTEDGE <> 0 then
      Style := Style and not WS_EX_CLIENTEDGE
    else
      Exit;
    SetWindowLong(ClientHandle, GWL_EXSTYLE, Style);
    SetWindowPos(ClientHandle, 0, 0, 0, 0, 0, SWP_FRAMECHANGED or SWP_NOACTIVATE or
      SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER);
  end;
end;

procedure TForm1.ClientWndProc(var Message: TMessage);
begin
  if Message.Msg = $3F then
    begin
      ShowMDIClientEdge(ClientHandle, false);
      Exit;
    end;

  inherited;
end;

但是 MDI 表单出现在我所居住的组件下方的第二个问题无法真正修复。这使得该方法更具吸引力,如果表单能够正常行动并引起焦点。fsNormal

简而言之,我需要另一个表单中的表单,它可以:

  • 留在客户端区域内,因此它不能离开或被吸引到客户端之外
  • 显示在窗体内的控件上方
  • 专注力强

那么,为什么这些表格无法引起焦点呢?fsNormal

表格 Delphi

评论

2赞 Remy Lebeau 11/15/2023
您是否考虑过使用 TFrame 而不是 TForm?除了 MDI(尽管 Delphi 12 重新设计了它)之外,TForm 从未打算作为子项嵌入。TFrame 是专门为嵌入式而设计的。
0赞 Codrut 11/15/2023
@RemyLebeau我会使用框架,但我要求窗口具有对齐、窗口状态和显示标题栏的移动性。最好是能够继承 VCL 主题标题栏的标题栏。
1赞 Tom Brunberg 11/16/2023
这会对您有所帮助吗: 如果是子窗体,请编写一个事件处理程序,在其中调用 和 .fsNormalOnMouseDownSelf.BringToFrontSelf.SetFocus
0赞 Codrut 11/16/2023
@TomBrunberg 这是一个进步。但不幸的是,当我单击表单上的组件时,它不再起作用。我还尝试覆盖 WndProc,不幸的是它仍然不起作用
1赞 BrakNicku 11/20/2023
让它在事件中工作应该很容易。如果需要,请查找 和 .检查单击的控件(或其所有者或遍历父控件),并执行上述建议。Application.OnMessageWM_LBUTTONDOWNWM_NCLBUTTONDOWN

答:

1赞 Ravenis 11/25/2023 #1

我首先用雅典创建了你的设置,然后我用亚历山大进行了测试。fsNormal

子窗体 OnCreate 过程 我刚刚添加了代码,其中 Form2 父项设置为 Form1。

procedure TForm2.FormCreate(Sender: TObject);
begin
  Parent := Form1;
end;

在亚历山大的焦点上,点击窗体的问题,除了它的标题栏之外,可以通过将其放在其事件的前面来解决。 D12 不再有这个问题了。OnMouseDown

procedure TForm2.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  BringtoFront;
end;

一个烦人的问题仍然存在,那就是当您直接在其标题栏上移动表单时,它将保留在其他子表单下,直到您单击它。但是我通过添加它解决了 Windows 事件上的这个问题。此外,您还提到子表单不应超出 Form1 范围,并且也可以在事件中轻松解决。WMMovingBringToFrontWMMoving

procedure TForm2.WMMoving( var msg : TWMMoving );
  var
    moveArea : TRect;
  begin
    BringtoFront;
    moveArea := Form1.BoundsRect;

    with msg.DragRect^ do
    begin
      if Left < moveArea.Left
      then
        OffsetRect( msg.DragRect^, moveArea.Left - Left, 0 );

      if Top < moveArea.Top
      then
        OffsetRect( msg.DragRect^, 0, moveArea.Top - Top );

      if Right > moveArea.Right
      then
        OffsetRect( msg.DragRect^, moveArea.Right - Right, 0 );

      if Bottom > moveArea.Bottom
      then
        OffsetRect( msg.DragRect^, 0, moveArea.Bottom - Bottom );
    end;
    inherited;
end;

使用 Windows 事件时,您还需要将其添加到 Form 类中。

procedure WMMoving( var msg : TWMMoving ); message WM_MOVING;

例如,当单击窗体内的按钮而不是窗体本身时,子窗体没有将其更改为顶部的问题仍然存在。 一种选择是在鼠标进入表单时将表单放在前面。

procedure TForm2.FormMouseEnter(Sender: TObject);
begin
  SetFocus;
  BringtoFront;
end;

这些适用于 D11 和 D12,我没有任何旧版本可供测试。

此外,这个旧组件仍然有效。

https://torry.net/components/forms/other/ttopmost

我希望这能帮助你推进你的项目。

评论

0赞 Codrut 11/26/2023
这解决了大多数问题。但是 Form 方法失败了,因为它只有在您单击窗体客户端时才会执行,因此如果您单击窗体中的控件,则不会被注意到。将所有控件设置为相同的过程将起作用,虽然不理想,但功能正常。我认为无法解决的最后一个问题是标题栏文本在表单聚焦时不会改变颜色,但您并不总是拥有好东西。感谢您的详细回复!OnMouseDownOnMouseDown
0赞 Ravenis 11/27/2023
我添加了某种解决方案来回答第一个问题。