Using SetWindowPos with multiple monitors
System Displays disposition and VirtualScreen
In a Windows System, the Primary Screen (programming perspective) is the Display device which has its upper left corner position set at Point(0,0)
.
This implies that the Displays positioned on the left of the Primary Screen, will have negative X
coordinates (the Y
coordinate could be negative if the Display is in Portrait layout).
The Displays on the right will have positive X
coordinates (the Y
coordinate could be negative if the Display is in Portrait layout).
Displays on the Left of the Primary Screen:
In other words, Displays that have a negative Point.X
origin.
The Point.X
origin is the sum of all of the preceding Screens[].Width
, subtracted from the Point.X
origin coordinate of the Primary Screen.
Displays on the Right of the Primary Screen:
In other words, Displays that have a positive Point.X
origin.
The Point.X
origin is the sum of all of the preceding Screens[].Width
, Primary included, added to the origin Point.X
coordinate of the Primary Screen.
Important note about Dpi Awareness:
If the application is not DPI Aware, all these measures can be compromised by the virtualization and automatic DPI Scaling performed by the System. All measures will be uniformed to a default 96 Dpi: the application will receive scaled values. This also includes the values retrieved from non-Dpi ware Win32 API functions. See:
High DPI Desktop Application Development on Windows
Enable support for all targeted Systems in the app.manifest
file, uncommenting the required sections.
Add/Uncomment the DpiAware and DpiAwareness sections in the app.manifest
file.
The PerMonitorV2 Dpi Awareness mode can be set in the app.config
file (available from Windows 10 Creators Edition).
See also:
DPI and Device-Independent Pixels
Mixed-Mode DPI Scaling and DPI-aware APIs
Example:
Consider a System with 3 Monitors:
PrimaryScreen (\\.\DISPLAY1): Width: (1920 x 1080)
Secondary Display (Right) (\\.\DISPLAY2): Width: (1360 x 768)
Secondary Display (Left) (\\.\DISPLAY3): Width: (1680 x 1050)
PrimaryScreen:
Bounds: (0, 0, 1920, 1080) Left: 0 Right: 1920 Top: 0 Bottom: 1080
Secondary Display (Right):
Bounds: (1360, 0, 1360, 768) Left: 1360 Right: 2720 Top: 0 Bottom: 768
Secondary Display (Left):
Bounds: (-1680, 0, 1680, 1050) Left: -1680 Right: 0 Top: 0 Bottom: 1050
If we change, using the System applet, the Primary Screen reference, setting it to \\.\DISPLAY3
, the coordinates will be modified accordingly:
Virtual Screen
The Virtual Screen is a virtual display, which dimensions are represented by:
Origin: the origin coordinate of the left-most Screen
Width: the sum of all the Screens
Widths.
Height: the Height of the highest Screen
.
These measure are reported by SystemInformation.VirtualScreen
The Primary Screen Size
is reported by SystemInformation.PrimaryMonitorSize
All the Screens current measures and position can also be retrieved using Screen.AllScreens and inspecting each \\.\DISPLAY[N]
properties.
Using the preceding example as reference, in the first disposition, the VirtualScreen
bounds are:
Bounds: (-1680, 0, 3280, 1080) Left: -1680 Right: 3280 Top: 0 Bottom: 1080
In the second disposition, the VirtualScreen
bounds are:
Bounds: (0, 0, 4960, 1080) Left: 0 Right: 4960 Top: 0 Bottom: 1080
Window Position inside a Display area:
The Screen class offers multiple methods that can be used to determine in which screen a specific window is currently displayed:
Screen.FromControl([Control reference])
Returns the Screen
object that contains the largest section of the specified Control
reference.
Screen.FromHandle([Window Handle])
Returns the Screen
object that contains the largest section of the Window\Control referenced by an Handle
Screen.FromPoint([Point])
Returns the Screen
object that contains a specific Point
Screen.FromRectangle([Rectangle])
Returns the Screen
object that contains the largest section of the specified Rectangle
Screen.GetBounds()
(overloaded)
Returns a Rectangle
structure that references the Screen Bounds that contain:
- a specific
Point
- largest section of the specified
Rectangle
- A
Control
reference
To determine the \\.\DISPLAY[N]
in which the current Form is shown, call (for example):
Screen.FromHandle(this);
To determine in which Screen a secondary Form is shown:
(Using the Displays layout shown in the sample images)
var f2 = new Form2();
f2.Location = new Point(-1400, 100);
f2.Show();
Rectangle screenSize = Screen.GetBounds(f2);
Screen screen = Screen.FromHandle(f2.Handle);
screenSize
will be equal to the \\.\DISPLAY3
Bounds.screen
will be the Screen
object representing the \\.\DISPLAY3
properties.
screen
object will also report the \\.\DISPLAY[N]
name of the Screen
in which form2
is shown.
Obtain the hMonitor
Handle of a Screen object:
The .NET Reference Source shows that the hMonitor
is returned calling [Screen].GetHashCode();
IntPtr monitorHwnd = new IntPtr([Screen].GetHashCode());
Or using the same native Win32 functions:
MonitorFromWindow, MonitorFromPoint and MonitorFromRect
[Flags]
internal enum MONITOR_DEFAULTTO
{
NULL = 0x00000000,
PRIMARY = 0x00000001,
NEAREST = 0x00000002,
}
[DllImport("User32.dll", SetLastError = true)]
internal static extern IntPtr MonitorFromWindow(IntPtr hwnd, MONITOR_DEFAULTTO dwFlags);
[DllImport("User32.dll", SetLastError = true)]
internal static extern IntPtr MonitorFromPoint([In] POINT pt, MONITOR_DEFAULTTO dwFlags);
[DllImport("User32.dll", SetLastError = true)]
internal static extern IntPtr MonitorFromRect([In] ref RECT lprc, MONITOR_DEFAULTTO dwFlags);
- To detect Window movements between Monitors, you can handle
WM_WINDOWPOSCHANGED
messages, callMonitoFromWindow
, then GetScaleFactorForMonitor to determine whether there's a DPI change and react to a new setting, eventually.
Obtain a Handle of the device context of a Screen:
A generic method to retrieve the hDC of any Display available.
The Screen coordinates or Screen Device can determined using one of the methods previously described when only a specific Screen reference is required.
The Screen.DeviceName property can be used as the lpszDriver
parameter of GDI's CreateDC function. It will return the hDC of the display that Graphics.FromHdc can use to create a valid Graphics object, which will allow to paint on a specific screen.
Here, assuming at least two Displays are available:
[DllImport("gdi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData);
[DllImport("gdi32.dll", SetLastError = true, EntryPoint = "DeleteDC")]
internal static extern bool DeleteDC([In] IntPtr hdc);
public static IntPtr CreateDCFromDeviceName(string deviceName)
{
return CreateDC(deviceName, null, null, IntPtr.Zero);
}
Screen[] screens = Screen.AllScreens;
IntPtr screenDC1 = CreateDCFromDeviceName(screens[0].DeviceName);
IntPtr screenDC2 = CreateDCFromDeviceName(screens[1].DeviceName);
using (Graphics g1 = Graphics.FromHdc(screenDC1))
using (Graphics g2 = Graphics.FromHdc(screenDC2))
using (Pen pen = new Pen(Color.Red, 10))
{
g1.DrawRectangle(pen, new Rectangle(new Point(100, 100), new Size(200, 200)));
g2.DrawRectangle(pen, new Rectangle(new Point(100, 100), new Size(200, 200)));
}
DeleteDC(screenDC1);
DeleteDC(screenDC2);
SetWindowPos and multiple monitors with different resolutions
The issue isn't with coordinate transformation. It is that windows isn't allowing SetWindowPos to adjust the window so it is larger than the screen. Of course, it is basing this on the primary monitor size.
See: Can a window be resized past the screen size/offscreen?
Related Topics
Output Log Using Ftpwebrequest
How to Match an Entire String With a Regex
How to Escape a Double Quote in a Verbatim String Literal
Why Would You Use Expression≪Func≪T≫≫ Rather Than Func≪T≫
How to Convert Json Object to Custom C# Object
Asp.Net MVC 5 Culture in Route and Url
How to Create a New Object Instance from a Type
What's the Use/Meaning of the @ Character in Variable Names in C#
Getting All Types That Implement an Interface
Transitioning from Windows Forms to Wpf
A Field Initializer Cannot Reference the Nonstatic Field, Method, or Property
Using Setwindowpos With Multiple Monitors
How to Dynamically Create a Class
How to Suspend Painting For a Control and Its Children
Could Not Load File or Assembly or One of Its Dependencies
Addtransient, Addscoped and Addsingleton Services Differences