我已经建立了这个问题的MCV,我很乐意上传它。我将首先尝试描述问题。
创建一个主窗口,并通过任何可用的数据库将TDBGrid连接到表。窗口的OnShow连接到数据库并打开表。
在主窗口上创建一个启动非模态窗口的按钮。
在非模态窗口上创建一个启动模态窗口的按钮。
请仔细按照以下步骤复制问题。
运行该应用程序。
将焦点放入网格并使用鼠标滚轮向上和向下滚动。
按按钮启动非模态窗口。
当非模态窗口打开时,单击主窗口中的网格,然后再次使用鼠标滚轮向上和向下滚动。
虽然仍然专注于网格,但请点击非模态窗口上的按钮以启动模态窗口。
当模态窗口打开时,将鼠标悬停在网格上并使用鼠标滚轮。您将看到网格向上和向下滚动。
这在Windows 7上不会发生,但在Windows 10上不会发生。它可能看起来无害,但是当您在3个窗口中构建了几层父子关系时,它会特别危险。
让我们说模态窗口包含主窗口的子孙。如果用户启动模态窗口的目的是编辑特定的子孙,并且意外地使用他们的鼠标滚轮并在主窗口上移动祖母,他们现在正在编辑他们并不想要的孙子。
应该注意的是,在步骤4和5之间,如果在启动模式窗口之前没有将焦点放在网格中,则不会发生此问题。我已经尝试以编程方式将焦点设置到非模态窗口的控件上,然后显示模态窗口没有成功。
答案 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;