Grid Scroll Behind Modal Window - delphi

I have built a MCV of this problem, and I'm happy to upload it. I'll try to describe the problem first.
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.
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.
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.
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.

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;

Related

Disable popup on a context menu?

How can I disable popup of a popup menu?
My problem is that I have a nice context menu for my listbox but if I shift-rightclick an item, I execute some code, and I don't want the popup menu popping up afterwards.
But if it is a normal rightclick on the listbox, then of course I want the popup menu.
There's no "OnContextPopup" in Delphi, and no "Handled" parameter either. One way of doing this in Delphi is as follows:
Locate the PopupMenu on your form, click it, go to the events tab of the object inspector, double-click the cell to the right of "OnPopup" and insert the following code (the line right after begin):
PROCEDURE TForm1.PopupMenu1Popup(Sender : TObject);
BEGIN
IF GetKeyState(VK_SHIFT) AND $8000<>0 THEN Abort
END;
this will suppress the popup menu, if any of the two SHIFT keys are pressed when the menu is about to pop up...
Another way (spurred by TLama's comment above) is to locate the OnContextPopup event for the control you want to suppress the popup for (in this case your ListBox), double click it (to create/jump to the event handler) and insert the following code:
PROCEDURE TForm1.ListBox1ContextPopup(Sender : TObject ; Point : TPoint ; VAR Handled : BOOLEAN);
BEGIN
Handled:=(GetKeyState(VK_SHIFT) AND $8000<>0)
END;
The main difference between these two methods is that the first one suppresses the popup, no matter which control the popup menu is attempting to pop up over, whereas the second method allows you to only suppress the popup for specific controls (as you can assign the same popup menu to several controls).

Appearing With Effect, When Showing Window A Second Time

I need to know the reason (and possible workarounds) for a strange behavior in Windows 7.
I have Form1 with a button on it and also a second form called Form2. In button's click handler I've:
Form2.Show;
After running my program, the first time I click on the button, my Form2 appears with a nice fade-in effect (sorry, I don't know the exact effect name in Windows 7. Tell me if you know!). OK. I close Form2 and click the button again. This time Form2 appears with no effect at all.
I want my Form2 to appear with that effect every time I click on the button. To be more specific, I need the main form of a real application to appear when user clicks on a tray icon. The first time clicking on the tray icon shows the window with animation, but second time it doesn't. How can I solve that?
Windows shows that animation the first time a window is shown. So all you need to do is make sure that every time your form is shown, the associated window is being shown for the first time.
You could destroy the form when it closes and create a new instance when you need to show it again. However, that may be inconvenient for you depending on how your form manages state. Judging from your edit and comments, you cannot afford to destroy the form when you close it. Instead you would need to force a new window to be created for your form, each time you show it.
For example, add a call to DestroyHandle in the OnClose event of the form. Or make a call to RecreateWnd immediately before you show the window. Note that the latter will involve making the protected method RecreateWnd visible to whoever calls Show on the form.
Remove your Form2 from the auto-create list. (Project->Options->Forms, click Form2 in the left pane, and click the button labeled > to move it to the right side.)
Change your ButtonClick handler:
procedure TForm1.Button1Click(Sender: TObject);
var
TempFrm: TForm2;
begin
TempFrm := TForm2.Create(nil);
TempForm.Show;
end;
Add a FormClose event to TForm2 (while you're at it, delete the global Form2: TForm2; variable just above the implementation clause, so you don't use it again by mistake)::
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
As a note: You should go to Tools->Options->VCL Designer, and uncheck Auto create forms and data modules in the Module creation options at the bottom. Auto-creating forms is almost always a bad idea; the only thing I ever allow to be auto-create (other than the main form) is any TDataModule that needs to be available immediately, and I move it to the top of the auto-create list you saw in the first paragraph so that it's created before the main form.

How to avoid the "Open IME" popup in a StringGrid?

In a StringGrid, sometimes I get the unwanted menu below when I right-click. Is this a Windows popup?
How to I prevent this popup from appearing rather than my own?
I have goAlwaysShowEditor in my Options.
I have set StringGrid.PopupMenu to my popup.
I've set StringGrid.OnMouseDown to show my popup if it's a right click.
You can override the virtual CreateEditor method like this way (not a good solution though, I know :-):
type
TStringGrid = class(Grids.TStringGrid)
protected
function CreateEditor: TInplaceEdit; override;
end;
implementation
function TStringGrid.CreateEditor: TInplaceEdit;
begin
Result := inherited CreateEditor;
TMaskEdit(Result).PopupMenu := Form1.PopupMenu1;
end;
That is the popup menu found in every Windows EDIT control. Possible the world's most known menu (the only competition comes from the system menu). You want it, because your user's expect it (and need it). When you edit the text in a cell, the TStringGrid control actually creates a standard Windows EDIT control, which is great. And thus you get its popup menu.
In addition, to show your own popup menu (when you are not editing a cell), you don't need to set the OnMouseDown handler. It is enough to set the PopupMenu property. In fact, it is very bad to use the OnMouseDown handler to trigger a popup menu, because then the menu will only be shown when the user right-clicks the control (and not, for instance, when he presses the "context" button on his keyboard).
If you really want your own popup menu to show, even when the user is editing a cell, you really have to give him his usual options for undo, copy, cut, paste, Unicode stuff, etc., manually. Surely you don't want that?

AdvToolbar from MDIChild displayed on MainForm and possibility to drag it all around

I've created an application which consists of a MainForm and several MDIChild Forms. I'm using AdvDockPanel and AdvToolbar from TMS' components. My main form has AdvDockPanels on Top, Bottom, Left and Right side of the Form to display AdvToolbars.
Each MDIChild Forms contains an AdvToolbar and at creation of theses forms the AdvToolbar.Parent is set to the AdvDockPanel on the MainForm. At first, the AdvToolbar.Visible is False and on activation of each MDIChild Form I'm setting it's visibilty to True.
To sum up, on activation of an MDIChild Form I want it's AdvToolbar to be displayed on MainForm.AdvDockPanel and on deactivation of it I want it to hide.
This all works fine except when I try to drag an AdvToolbar from one AdvDockPanel to an other will crossing the middle of my MainForm which makes my active MDIChild form hidden and that causes this error: "Cannot change Visible in OnShow or OnHide".
I've debugged it and found where this Exception is raised. It's in Vcl.Forms.
procedure TCustomForm.CMShowingChanged(var Message: TMessage);
So, I'd like to know if there is a way to display the AdvToolbar from my active MDIChild Form on the MainForm while being able to drag my toolbar all around in my MainForm.
Thanks,
Charles
You could check if the current parent of the toolbar is a TFloatingWindow (thats the parent when dragging the toolbar):
procedure TForm25.FormDeactivate(Sender: TObject);
begin
if not (orgaadvtoolbar1.Parent is TFloatingWindow) then
OrgaAdvToolBar1.visible:=false;
end;
TAdvToolBar.Dragging doesn't work because TAdvToolbar uses an own dragging flag FDraging but its not a property, so you could only access the variable if you modify the sourcecode of TMS Software.

Delphi form minimise issue

I currently have a delphi 7 project sitting in front of me and what the original creators of the software have done is used the main form as a launch pad for another form which contains the actual controls and logic behind the entire application. So basically, form1 loads up, is set to invisible and another form (the form with all the UI controls and logic) is created and shown, its a strange way to do things, but its the way they did it.
Now I'm not familiar with the way delphi 7 handles its forms, but this second window, the window with all the controls on, whenever i click the minimise button, the form does not drop down to the taskbar as one would expect, but rather, resizes so that only the minimise, maximise and close buttons are visible and then places itself at the bottom left of the screen, just above the start menu.
the creation of this second window is:
frmPlatform := TfrmPlatform.Create(frmMain);
ModalResult := frmPlatform.ShowModal;
where frmMain is the invisible form.
My question is, why does the second window not minimize as one would expect and drop to the taskbar? and how do i get it to work,
thanks
Standard behaviour of forms I'm afraid as the mainform is by default the only form shown by Delphi on the taskbar. You can however set other forms to appear on the taskbar as well:
procedure TForm1.CreateParams
(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.ExStyle := Params.ExStyle
or WS_EX_APPWINDOW;
end;
And you can hide your current main form of course as well, see: http://delphi.about.com/od/adptips1999/qt/hidefromtaskbar.htm, and http://delphi.about.com/od/delphitips2008/qt/hide_taskbutton.htm for D2007 and up.
I think you will find that in standard Delphi apps only the main (first) form will be shown in the task bar. Since your main form is invisible that's your issue. The minimize is probably due to the second form being called with show modal rather than simply show.
Can you remove the first form or change which form is the main form? This can be done from the project settings page.

Resources