Grid Scroll Behind Modal Window

和自甴很熟 提交于 2019-12-09 06:50:27

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;

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


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;

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;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!