My WPF application is exhibiting strange behavior on my two monitor laptop development system. The second monitor has a resolution of 1920 x 1080; the laptop\'s resolution is 1
I've finally resolved this problem. It turns out that what I needed to do is change one line in the switch
statement in the WindowProcedureHook
method:
case WinMessages.WM_GETMINMAXINFO:
// lParam has a pointer to the MINMAXINFO structure. Marshal it into managed memory.
MINMAXINFO mmi = (MINMAXINFO) Marshal.PtrToStructure( lParam, typeof( MINMAXINFO ) );
if ( monitor != IntPtr.Zero ) {
MONITORINFO monitorInfo = new MONITORINFO();
GetMonitorInfo( monitor, monitorInfo );
// Get the Monitor's working area
RECT rcWorkArea = monitorInfo.rcWork;
RECT rcMonitorArea = monitorInfo.rcMonitor;
// Adjust the maximized size and position to fit the work area of the current monitor
mmi.ptMaxPosition.x = Math.Abs( rcWorkArea.left - rcMonitorArea.left );
mmi.ptMaxPosition.y = Math.Abs( rcWorkArea.top - rcMonitorArea.top );
mmi.ptMaxSize .x = Math.Abs( rcWorkArea.right - rcWorkArea.left );
mmi.ptMaxSize .y = Math.Abs( rcWorkArea.bottom - rcWorkArea.top );
}
// Copy our changes to the mmi object back to the original
Marshal.StructureToPtr( mmi, lParam, true );
handled = false; // This line used to set handled to true
return IntPtr.Zero;
With this change, the code that's normally executed in WPF when the WM_GETMINMAXINFO
message is received still runs, but it uses the change to the MINMAXINFO
object made by the code in order to do its work. With this change, the WPF window handles the resolution changes properly.
EDIT
And it turns out that the code no longer needs to look specifically for a screen resolution or installed monitor change. That is, the SystemEvent.DisplaySettingsChanged
event handler is no longer needed.
Turns out its not a complicated fix. The MinTrackSize point (bounds) needs to be set to the working area dimensions of the secondary monitor.
private static void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam)
{
MINMAXINFO mmi = (MINMAXINFO)System.Runtime.InteropServices.Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
/* 0x0001 // center rect to monitor
0x0000 // clip rect to monitor
0x0002 // use monitor work area
0x0000 // use monitor entire area */
int MONITOR_DEFAULTTONEAREST = 0x00000002;
System.IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (monitor != System.IntPtr.Zero)
{
MONITORINFO monitorInfo = new MONITORINFO();
GetMonitorInfo(monitor, monitorInfo);
RECT rcWorkArea = monitorInfo.rcWork;
RECT rcMonitorArea = monitorInfo.rcMonitor;
// set the maximize size of the application
mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
// reset the bounds of the application to the monitor working dimensions
mmi.ptMaxTrackSize.x = mmi.ptMaxSize.x;
mmi.ptMaxTrackSize.y = mmi.ptMaxSize.y;
}
System.Runtime.InteropServices.Marshal.StructureToPtr(mmi, lParam, true);
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct MINMAXINFO
{
public POINT ptReserved;
public POINT ptMaxSize;
public POINT ptMaxPosition;
public POINT ptMinTrackSize;
public POINT ptMaxTrackSize;
};