Delphi - 应用程序主窗体获取焦点不正确

4
在我的应用程序的两个部分中,主窗体在错误的时刻获得焦点:
Bug 1. 当从特定打印机表单点击“确定”时。
  1. 我打开一个FastReports PDF预览 - 这是第一个弹出窗口。这个窗口没有在任务栏中单独显示。这个窗体是模态的。
  2. 然后我点击打印,
  3. 那会打开另一个具有标准打印选项的窗口。
  4. 然后我点击属性 - 这将打开驱动程序的特定表单。我更改了双重打印设置。
  5. 当我点击“确定”时,预览窗体(1)应该获得焦点,但是主窗体被带到前面。因为预览窗体仍然是模态的,所以很难回到预览窗体。只有通过随机点击,才能再次将预览窗体聚焦。

clicking through


问题2. 点击或拖动此特定滚动框会将主窗体聚焦

  1. 此窗口处于活动状态。这是Windows任务栏中的一个分离窗口,不是模态的。在此表单上有一个gnostice pdf阅读器。
  2. 当我点击滚动框开始拖动时,主窗体被置于前台。当我继续拖动时,pdf表单上的滚动仍然继续。此外,鼠标附近的工具提示显示当前显示的页面。

Bug 2

我希望解决这种奇怪的行为。因此,我的问题是:
什么可能导致这些聚焦错误?
关于应用程序:
- 我已经注意到:弹出窗口的表单似乎有点太大了,你可以在这里看到: form seems to large - 见编辑 - 一些表单是 MDI 子窗体。 - 我在代码中搜索了所有鼠标事件并设置断点,但该代码在出现两个错误时没有执行。 - 使用了 VCLskin。 - Bug 1 出现在 Fast reports 版本 5.6.1 和 5.6.8 中。 - Windows 10。 - Delphi XE 10.2。 编辑 - 应用程序主窗体已正确设置。启动时,首先会显示一个登录窗体。登录后,数据模块会被创建,一些表单会被创建而没有所有者(它们会在应用程序结束时释放)。

除了主表单之外的所有其他表单,都是由应用程序而不是登录表单所拥有的。确切地说,它们不是父级,而是所有者,就像@uwe-raabe所说的那样。

然后创建主表单。这是通过登录表单创建的:

Frm_DatabaseLogin.CreateForm(TFrm_MainMenu, Frm_MainMenu);

那调用:

procedure TFrm_DataBaseLogin.CreateForm(InstanceClass: TComponentClass;
  var Reference);
begin
  Updateprogress(InstanceClass.ClassName);
  Application.CreateForm(InstanceClass,Reference);
end;

在UpdateProgress中,没有什么特别的事情发生。

之后,其他窗体也被创建,由应用程序拥有。最后,登录窗体隐藏,因此显示主窗体。


2
然后创建主窗体。它的父窗体是登录窗体,这听起来有些奇怪。你是不是想说“所有者”而不是“父窗体”?此外,主窗体的所有者通常是应用程序,并且在 DPR 中通过调用 Application.CreateForm 创建主窗体。你能展示一下创建这些窗体的代码吗? - Uwe Raabe
虽然这个问题可能非常有趣,但如果没有应用程序代码,如何回答这个问题呢?有关调试的想法呢?也许可以看一下 TWinControl.SetZOrderPositionTWinControl.WMSetFocusTCustomForm.WMActivate。在例如 WMSetFocus 中使用 实际中断 的断点可能会改变执行,因为应用程序和 IDE 之间的焦点更改会发生。对于 WMSetFocusSetZOrderPosition,我会使用断点条件,比如 Self is TCustomForm - nil
相当概括的问题,具有相当特定的背景。 "这可能是什么原因导致的......" ... 可能是错误的代码... 在我们看不见的某个地方。 - Sertac Akyuz
1个回答

1

我在这里对你的启动代码做了一些假设。

"主窗体"可能是一个令人困惑的术语。从TApplication的角度来看,Application.MainForm始终是使用Application.CreateForm创建的第一个窗体。

由于您的登录窗体创建了应用程序的主要窗体然后隐藏自己,因此登录窗体仍然是"主"窗体。

您的屏幕截图显示了任务栏上显示的两个图标。我假设您正在覆盖CreateParams或调用SetWindowLong以实现这一点。

我的设置与您类似,具有一个"主"登录窗体,然后将其隐藏。

在我的应用程序中,我为应该是独立的并且具有任务栏图标的窗体覆盖CreateParams:

procedure TMgrMain.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
  Params.WndParent := 0;
end;

当显示弹出窗口时,我会创建一个带有所有者的表单(可能不需要),然后设置PopupMode和PopupParent。自从我开始这样做,我就再也没有遇到弹出窗口被放在后面的情况了。

procedure ShowAbout(Owner: TForm);
var
  LocalForm: TAbout;
begin
  LocalForm := TAbout.Create(Owner);
  try
    LocalForm.PopupMode := pmExplicit;
    LocalForm.PopupParent := Owner;
    LocalForm.ShowModal;
  finally
    FreeAndNil(LocalForm);
  end;
end;

来自弹出窗口父级帮助:

如果PopupMode属性设置为pmExplicit且PopupParent为nil,则Application.MainForm隐式用作PopupParent。 如果未分配Application.MainForm,则使用Application.Handle作为PopupParent。

如果PopupMode属性设置为pmAuto,则使用Screen.ActiveForm作为PopupParent属性。

我所采取的步骤部分来源于Peter Below的新闻组帖子。此外,这是非常古老的建议,是在添加PopupParent / PopupMode之前的。

新闻组: borland.public.delphi.winapi
发件人: "Peter Below (TeamB)" <100113.1...@compuXXserve.com>
日期: 2000/11/30
主题: Re: Modeless window act like .exe

.. 省略 ..
注意,这可能会导致从次要窗体显示的模态窗体出现一些问题。如果用户在模态窗体弹出时切换到应用程序之外,然后返回显示它的窗体,模态窗体可能会隐藏在窗体下面。可以通过确保模态窗体是由显示它的窗体作为父窗体(使用上述的 params.WndParent)来处理这个问题,但这对于对话框单元中的标准对话框和异常来说是不可能的,需要更多的努力才能使它们正常工作(基本上是处理 Application.OnActivate,查找通过 GetLastActivepopup 与 Application 相关联的模态窗体,并通过 SetWindowPos 将它们置于 Z-顺序的顶部)。
.. 省略 ..

最后,这里有一篇博客文章,讲述了为什么要对PopupMode和PopupParent进行更改。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接