模态窗口背后的网格滚动

时间:2016-05-19 15:53:56

标签: delphi windows-10 delphi-xe3

我已经建立了这个问题的MCV,我很乐意上传它。我将首先尝试描述问题。

  1. 创建一个主窗口,并通过任何可用的数据库将TDBGrid连接到表。窗口的OnShow连接到数据库并打开表。

  2. 在主窗口上创建一个启动非模态窗口的按钮。

  3. 在非模态窗口上创建一个启动模态窗口的按钮。

  4. 请仔细按照以下步骤复制问题。

    1. 运行该应用程序。

    2. 将焦点放入网格并使用鼠标滚轮向上和向下滚动。

    3. 按按钮启动非模态窗口。

    4. 当非模态窗口打开时,单击主窗口中的网格,然后再次使用鼠标滚轮向上和向下滚动。

    5. 虽然仍然专注于网格,但请点击非模态窗口上的按钮以启动模态窗口。

    6. 当模态窗口打开时,将鼠标悬停在网格上并使用鼠标滚轮。您将看到网格向上和向下滚动。

    7. 这在Windows 7上不会发生,但在Windows 10上不会发生。它可能看起来无害,但是当您在3个窗口中构建了几层父子关系时,它会特别危险。

      让我们说模态窗口包含主窗口的子孙。如果用户启动模态窗口的目的是编辑特定的子孙,并且意外地使用他们的鼠标滚轮并在主窗口上移动祖母,他们现在正在编辑他们并不想要的孙子。

      应该注意的是,在步骤4和5之间,如果在启动模式窗口之前没有将焦点放在网格中,则不会发生此问题。我已经尝试以编程方式将焦点设置到非模态窗口的控件上,然后显示模态窗口没有成功。

1 个答案:

答案 0 :(得分:3)

这是VCL代码中的错误。要在正常应用程序中复制问题,如评论中所述,需要启用Windows 10的非活动窗口滚动功能,或者在早期操作系统中提供类似功能的软件。

然而,可以在没有特殊要求的情况下证明问题。这将是一个比问题更简单的再现。

在表单上删除一个stringgrid和一个按钮,该按钮在其click处理程序中包含以下代码:

procedure TForm1.Button1Click(Sender: TObject);
begin
  StringGrid1.Enabled := False;
  SetFocusedControl(StringGrid1);
end;

单击按钮并将鼠标悬停在网格上并滚动。按原样禁用,网格不应滚动,但确实如此。

以下是对问题中案例的更恰当的问题再现。这是因为模态窗口禁用包含网格的表单,然后发布鼠标滚轮消息。轮子消息是针对没有上述要求的环境合成的。

procedure TForm1.Button1Click(Sender: TObject);
var
  Pt: TPoint;
begin
  Enabled := False;
  Pt := Point(1, 1);
  MapWindowPoints(StringGrid1.Handle, HWND_DESKTOP, Pt, 1);
  SetFocusedControl(StringGrid1);
  Perform(WM_MOUSEWHEEL, MakeWParam(0, WORD(-120)), MakeLParam(Pt.X, Pt.Y));
end;

观察到网格滚动后按Ctrl + F2。

问题的根本原因是,在确定控件是否为聚焦控件时,VCL不关心是否启用控件,并且在执行鼠标滚轮消息时。将鼠标滚轮消息变为CM_MOUSEWHEEL并完全绕过默认窗口过程,VCL应该执行这些检查。

要解决此问题,您可以在启动模式窗体之前将聚焦控件设置为其他控件:

MainForm.SetFocusedControl(MainForm);
OtherForm.ShowModal;

您无法在此处设置ActiveControl,因为该网站已进行必要的可见性/状态检查。

如果您不想与主表单耦合,可以将鼠标滚轮消息处理程序放到主表单中:

type
  TMainForm = class(TForm)
    ...
  protected
    procedure WMMouseWheel(var Message: TWMMouseWheel); message WM_MOUSEWHEEL;

...

procedure TMainForm.WMMouseWheel(var Message: TWMMouseWheel);
begin
  if IsWindowEnabled(Handle) then
    inherited;
end;
相关问题