提问人:Codrut 提问时间:11/15/2023 更新时间:11/15/2023 访问量:80
使表单内的表单在单击时成为焦点
Make forms inside a form catch focus when clicked
问:
在 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 上方。
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;
问题
MDI 表单非常适合我的用例,如果不是针对 2 个问题:
- 边框,称为“ClientEdge”
fsMDIForm
- 窗体内的控件显示在 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
答:
我首先用雅典创建了你的设置,然后我用亚历山大进行了测试。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 范围,并且也可以在事件中轻松解决。WMMoving
BringToFront
WMMoving
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
我希望这能帮助你推进你的项目。
评论
OnMouseDown
OnMouseDown
评论
fsNormal
OnMouseDown
Self.BringToFront
Self.SetFocus
Application.OnMessage
WM_LBUTTONDOWN
WM_NCLBUTTONDOWN