模态窗口后面的网格滚动 [英] Grid Scroll Behind Modal Window

查看:97
本文介绍了模态窗口后面的网格滚动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我为此问题制作了MCV,很高兴上传。我将尝试首先描述问题。

I have built a MCV of this problem, and I'm happy to upload it. I'll try to describe the problem first.


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

  1. Create a main window and place a TDBGrid connected to a table through any database available. OnShow of the window connect to the database and open the table.

在主窗口上创建一个按钮,以启动非模式窗口。

Create a button on the main window that launches a non-modal window.

在非模态窗口上,创建一个按钮,以启动模态窗口。

On the non-modal window create a button that launches a modal window.

请仔细执行以下步骤以复制问题。

Follow these steps carefully to replicate the problem.


  1. 运行应用程序。

  1. Run the application.

将焦点置于网格中,并使用鼠标滚轮上下滚动。

Put focus into the grid and use your mouse wheel to scroll up and down.

按下按钮以启动非模式窗口。

Press the button to launch the non-modal window.

打开非模式窗口时,请在主窗口中单击返回到网格,然后再次使用鼠标滚轮上下滚动。

While the non-modal window is open, click back into the grid in the main window and again use your mouse wheel to scroll up and down.

虽然仍聚焦在网格中,但单击非模态窗口上的按钮以启动模态窗口。

While still focused in the grid, click the button on the non-modal window to launch the modal window.

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

While the modal window is open, hover over the grid and use your mouse wheel. You'll see that the grid scrolls up and down.

在Windows 7上不会发生这种情况,但在Windows 10上却会发生。这看似无害,但特别危险当您在3个窗口中建立了几层父子关系时。

This does not happen on Windows 7, but does on Windows 10. It may seem innocuous, but it's particularly dangerous when you have a few layers of parent child relationships built across the 3 windows.

让我们说模式窗口包含主窗口的子孙。如果用户出于编辑特定孙子女的目的而启动了模式窗口,并且不小心使用了鼠标滚轮并在主窗口上移动了祖父母,那么他们现在正在编辑他们本不希望的孙子女。

Let's say the modal window contains grand-children of the main window. If the user launches the modal window with the intent of editing specific grand-children, and accidentally uses their mouse wheel and moves the grand-parent on the main window, they are now editing grand-children that they didn't intend to.

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

It should be noted that between step 4 and 5, if you do not put focus in the grid before launching the modal window, this problem does not occur. I have tried setting focus programmatically into a control on the non-modal window before showing the modal window with no success.

推荐答案

这是VCL代码中的错误。如注释中所述,要在普通应用程序中复制该问题,需要启用Windows 10的非活动窗口滚动功能,或在较早版本的OS中提供类似功能的软件。

This is an error in VCL code. To duplicate the problem in a normal application, as noted in the comments, one need to have Windows 10's inactive window scrolling feature enabled, or software providing similar functionality in earlier OS.

但是可以在没有特殊要求的情况下演示该问题。这比问题中的复制要简单得多。

However it is possible to demonstrate the problem without special requirements. This would be a simpler reproduction than in the question.

拖放一个字符串网格和一个表单上的按钮,该按钮的单击处理程序中将包含以下代码:

Drop a stringgrid and a button on a form, the button having the following code in its click handler:

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

单击按钮并将鼠标悬停在网格上并滚动。

Click the button and hover your mouse over the grid and scroll. Disabled as it is, the grid shouldn't scroll, but it does.

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

Below is a more appropriate problem reproduction to the case in the question. That's because the modal window disables the form containing the grid which is then posted the mouse wheel message. The wheel message is synthesized for environments that doesn't have the mentioned requirements.

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。

Press Ctrl+F2 after you observe that the grid scrolls.


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


The root cause of the problem is, VCL does not care if a control is enabled or not while deciding if it is the focused control, and while making it perform a mouse wheel message. Mutating the mouse wheel message to a CM_MOUSEWHEEL and completely bypassing the default window procedure, VCL should have been performing these checks.

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

For a workaround, you can set the focused control to a different control before launching the modal form:

MainForm.SetFocusedControl(MainForm);
OtherForm.ShowModal;

您不能在此处设置 ActiveControl ,因为该位置已经进行了必要的可见性/状态检查。

You can't set ActiveControl here, because that one has the necessary visibility/state check in place.

如果您不希望与主表单结合使用,则可以放置鼠标滚轮消息处理程序更改为主要形式:

If you don't want to be coupled with the main form, you can put a mouse wheel message handler to the main form:

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;

这篇关于模态窗口后面的网格滚动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆