I have an application which is designed for multiple monitors. It starts up, and we try to avoid activating windows that do not need to be activated, because the user only does keyboard input in one place, and each time we Activate a new form on a secondary monitor, it grabs keyboard focus, something we wish to avoid.
Our in-house base TForm class has a method like this, which is using the Win32 ShowWindow function directly, avoiding the VCL framework's internal visibility change system which grabs focus:
procedure TOurForm.ShowWithoutActivate;
begin
ShowWindow(Self.Handle, SW_SHOWNOACTIVATE);
Self.Visible := true;
end;
If I just did this, it would grab focus:
Self.Visible := true; // TWindow.Visible = true, will grab focus, plus make window visible.
This works, but the next thing I'd like to be able to do is set Maximized state so that the form will maximize itself on the Monitor that it is currently on. How do we get it onto a particular monitor? The same way it always worked, with modification of the Left and Top properties of the Form. We have to take care that if we store Left/Top/Width/Height on form, and then restore it, that the results are still valid when we reload it. That is NOT what I'm asking about.
I'm specifically asking about how to maximize the form now that I have "showed" it using the above custom function. One hack begets another hack. Here is how far down this rabbit hole I've gone:
Is it possible to both make this form visible and make it take the window state I want it to take without activating? If I can't do this, then users are going to lose their keyboard focus when I show this form on a secondary monitor, something that I really want to avoid.
What I tried is to use Win32 ShowWindow
API to do SW_SHOWMAXIMIZED
:
ShowWindow(Self.Handle, SW_SHOWMAXIMIZED);
The above seems to grab focus (activate).
When you create the top-level window set the extended window style to
WS_EX_NOACTIVATE | WS_EX_APPWINDOW
WS_EX_NOACTIVATE
stops the window activating. This also makes it disappear from the taskbar, so you need WS_EX_APPWINDOW
to fix that problem.
Call ShowWindow(hWnd, SW_MAXIMIZE)
and the window will be maximized but not activated.
You need to be able to activate the window once it is visible, so in the WM_ACTIVATE
handler (the irony!) you need to clear the WS_EX_NOACTIVATE
flag thus:
case WM_ACTIVATE:
{
DWORD exstyle = GetWindowLong(hWnd, GWL_EXSTYLE);
if (exstyle & WS_EX_NOACTIVATE)
{
SetWindowLong(hWnd, GWL_EXSTYLE, exstyle & ~(DWORD)WS_EX_NOACTIVATE);
}
}
Apologies for the C++. This should be simple to translate into Delphi.
EnumDisplayMonitors
API enumerates the monitors and their coordinates on the joint desktop, area into which a particular monitor maps to its specific position.
To find out which monitor is "current" you would want to compare the current window position against monitor coordinates rcMonitor
/rcWork
. Or, you have MonitorFromPoint
and friends to help you.
Once to decided which monitor you want, you can either move your window (MoveWindow
, SetWindowPos
) to monitor's work area, or use this rectangle in response to WM_GETMINMAXINFO
message to send the window to this position as a part of standard maximization.
To add to this, this small C++ application [1, 2, 3] demos the concept mentioned above, shows monitor information, and changes position where the window would be maximized to.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With