Introduction
For a long time, I was searching the web to solve the problem how to maximize / restore window on different monitor in multi-monitor setup, but did not find a direct way how to do this. Using information from several links, I finally get to the working and still simple solution.
Background
I noticed that if the window is moved to another monitor and then maximized, it is maximized on the correct monitor. However to do this in C++ was a harder task. My first (naive) approach was to restore the window (ShowWindow(SW_RESTORE)
, move it (calling SetWindowPos
or MoveWindow
) and then maximize it (ShowWindow(SW_MAXIMIZE)
. Such approach works, but adds some flickering of the window - more noticeable if window is already maximized on one monitor and it is necessary to move it maximized to another monitor.
Using the Code
If it is necessary to position the window within different monitors, it is necessary first to access rectangle in the screen coordinates of the target monitor. Note that pixel with coordinates [0, 0] is at the top-left corner of the primary monitor. Win32 API is very useful to access rectangles of the monitors:
MonitorFromPoint
MonitorFromRect
MonitorFromWindow
For completeness, I present first the naive approach how the window can be maximized on the specified monitor.
void Maximize(HWND hWnd, HMONITOR hMonitor)
{
MONITORINFO monitorInfo = { sizeof(MONITORINFO) };
GetMonitorInfo(hMonitor, &monitorInfo);
ShowWindow(hWnd, SW_RESTORE);
SetWindowPos(hWnd, nullptr, monitorInfo.rcMonitor.left,
monitorInfo.rcMonitor.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
ShowWindow(hWnd, SW_MAXIMIZE);
}
Flickering is more obvious if function is called several times with the same input window and same target monitor. Of course, someone can argue that it is possible to obtain the current state of the window and avoid calling the function (or making some early return in the function if the state is detected to be already correct). However such handling will just add more complexity to the function. I was thinking whether it is not possible to just hide the window and perform restoration of the state just somehow in the background and only at the end show the window. But trying to inject some SetRender(hWnd, FALSE)
or ShowWindow(SW_HIDE)
calls just made the output even worse. After some tests, I found that changing maximize/normal window changes just one bit (WS_MAXIMIZE
) in window's styles and finally with this information I get the final good solution:
void Maximize(HWND hWnd, HMONITOR hMonitor)
{
MONITORINFO monitorInfo = { sizeof(MONITORINFO) };
GetMonitorInfo(hMonitor, &monitorInfo);
const LONG currStyles = GetWindowLong(hWnd, GWL_STYLE);
SetWindowLong(hWnd, GWL_STYLE, currStyles | WS_MAXIMIZE);
const auto rc = monitorInfo.rcMonitor;
SetWindowPos(&CWnd::wndTop, rc.left, rc.top, rc.Width(), rc.Height(), 0);
}
And that's it. The function works as expected and if it is even called several times, there is no flickering. The function can be easily changed to also restore window to normal size by removing WS_MAXIMIZE
from the window styles and calling SetWindowPos
with correct rectangle information.
Links