c# - Usando SetWindowPos con mĂșltiples monitores
windows user32 (1)
Usando
user32.dll
y C # escribí el método que se ve a continuación.
Usando un identificador de proceso para una ventana, establecerá la posición de la ventana en una ubicación proporcionada
{x,y}
.
Sin embargo, en un entorno con múltiples monitores, el siguiente código establece la posición de la ventana en el monitor principal, solo.
Me gustaría poder seleccionar qué monitor, también.
¿Alguien puede explicar cómo se puede lograr esto usando
SetWindowPos
o quizás una combinación con otra función
user32.dll
?
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
private const int SWP_NOSIZE = 0x0001;
private const int SWP_NOZORDER = 0x0004;
private const int SWP_SHOWWINDOW = 0x0040;
public static void SetWindowPosition(Process p, int x, int y)
{
IntPtr handle = p.MainWindowHandle;
if (handle != IntPtr.Zero)
{
SetWindowPos(handle, IntPtr.Zero, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
}
}
Solución basada en el comentario de Jimi.
Aquí está mi configuración de monitor:
Observe que tengo un monitor secundario a la izquierda de mi monitor principal. Después de leer el enlace del monitor virtual que proporcionó Jimi, descubrí que para mover ventanas al monitor secundario debo usar un valor x negativo porque está a la izquierda del origen del monitor principal (esquina superior izquierda o <0,0>).
Por lo tanto, si quiero establecer la posición de mi ventana en la coordenada <0,0> del monitor secundario, debo SUBREGIR el ancho x del monitor secundario desde el origen del monitor primario, de esta manera:
<0,0> - <1920,0> = <-1920,0>
Ahora, cuando llamo a SetWindowPosition en mi código de cliente, lo llamo así:
SetWindowPosition(Process p, -1920, 0);
Nota: No sé qué haría si los monitores tuvieran diferentes resoluciones. Ese es un tema más complejo y no una pregunta que estoy haciendo. Además, no vi la necesidad de profundizar en el tema, ya que el sencillo ejemplo anterior resolvió todos mis problemas.
Sistema de disposición de pantallas y pantalla virtual.
En un sistema Windows, la
pantalla principal
(perspectiva de programación) es el dispositivo de visualización que tiene su posición en la esquina superior izquierda establecida en el
Point(0,0)
.
Esto implica que las pantallas ubicadas a la
izquierda
de la pantalla principal tendrán coordenadas
X
negativas
(la coordenada
Y
podría ser negativa si la pantalla está en diseño vertical).
Las pantallas de la
derecha
tendrán coordenadas
X
positivas
(la coordenada
Y
podría ser negativa si la pantalla está en diseño vertical).
Aparece a la
izquierda
de la pantalla principal
:
En otras palabras, las pantallas que tienen un
origen
Point.X
negativo
Point.X
El origen de
Point.X
es la suma de todas las
Screens[].Width
anteriores
Screens[].Width
, restado de la coordenada de origen de
Point.X
de la pantalla principal.
Aparece a la
derecha
de la pantalla principal
:
En otras palabras, las pantallas que tienen un
origen
Point.X
positivo
Point.X
El origen de
Point.X
es la suma de todas las
Screens[].Width
anteriores
Screens[].Width
,
Primaria incluida
, agregada a la coordenada
Point.X
origen de la Pantalla principal.
Nota importante
:
Si la aplicación no es DPIAware, todas estas medidas pueden verse comprometidas por la virtualización y el escalamiento automático de DPI que realiza el sistema.
Todas las medidas se
uniformarán
a un valor predeterminado de 96 Dpi: la aplicación recibirá valores escalados.
Esto también incluye los valores recuperados de funciones que no son DpiAware Win32 Api.
Ver:
Desarrollo de aplicaciones de escritorio High DPI en Windows
Habilite la compatibilidad con todos los sistemas específicos en el archivo
app.manifest
, sin comentar las secciones requeridas.
Agregue /
Descomente las secciones DpiAware y DpiAwareness
en el archivo
app.manifest
.
El modo de
conocimiento de Dpi PerMonitorV2
se puede configurar en el archivo
app.config
(disponible en Windows 10 Creators Edition).
Ejemplo:
Considere un sistema con 3 monitores:
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
Si cambiamos, usando el applet del sistema, la referencia de la pantalla principal, configurándola en
//./DISPLAY3
, las coordenadas se modificarán en consecuencia:
Pantalla virtual
La pantalla virtual es una pantalla virtual, cuyas dimensiones están representadas por:
Origen
: la coordenada de origen de la
Screen
más a la izquierda
Ancho
: la suma de todos los
Ancho
de
Screens
.
Altura
: la altura de la
Screen
más alta
Estas medidas son reportadas por
SystemInformation.VirtualScreen
SystemInformation.PrimaryMonitorSize
notifica el
Size
pantalla principal
Todas las medidas y posiciones actuales de las Pantallas también se pueden recuperar utilizando
Screen.AllScreens
e inspeccionando cada
//./DISPLAY[N]
propiedades.
Usando el ejemplo anterior como referencia, en la primera disposición, los límites de
VirtualScreen
son:
Bounds: (-1680, 0, 3280, 1080) Left: -1680 Right: 3280 Top: 0 Bottom: 1080
En la segunda disposición, los límites de
VirtualScreen
son:
Bounds: (0, 0, 4960, 1080) Left: 0 Right: 4960 Top: 0 Bottom: 1080
Posición de la ventana dentro de un área de visualización
:
La
clase Pantalla
ofrece múltiples métodos que se pueden usar para determinar en qué pantalla se muestra actualmente una ventana específica:
Screen.FromControl([Control reference])
Devuelve el objeto
Screen
que contiene la sección más grande de la referencia de
Control
especificada.
Screen.FromHandle([Window Handle])
Devuelve el objeto
Screen
que contiene la sección más grande de Window / Control referenciada por un
Handle
Screen.FromPoint([Point])
Devuelve el objeto
Screen
que contiene un
Point
específico
Screen.FromRectangle([Rectangle])
Devuelve el objeto
Screen
que contiene la sección más grande del
Rectangle
especificado
Screen.GetBounds()
(sobrecargado)
Devuelve una estructura de
Rectangle
que hace referencia a los Límites de pantalla que contienen:
- un
Point
específico
- Sección más grande del
Rectangle
especificado.
- Una referencia de
Control
Para determinar el
//./DISPLAY[N]
en el que se muestra el Formulario actual, llame (por ejemplo):
Screen.FromHandle(this);
Para determinar en qué Pantalla se muestra una Forma secundaria:
(Usando las pantallas de muestra en el ejemplo)
form2 = new Form2();
form2.Location = new Point(-1400, 100);
form2.Show();
Rectangle screenSize = Screen.GetBounds(form2);
Screen screen = Screen.FromHandle(form2.Handle);
screenSize
será = a
//./DISPLAY3
Bounds.
screen
será el objeto
Screen
que representa las propiedades
//./DISPLAY3
.
screen
objeto de
screen
también informará el nombre
//./DISPLAY[N]
de la
Screen
en la que se muestra
form2
.
Obtenga el controlador
hMonitor
de un objeto Screen
:
El
.NET Reference Source
muestra que se devuelve
hMonitor
llamando a
[Screen].GetHashCode();
IntPtr monitorHwnd = new IntPtr([Screen].GetHashCode());
O usando las mismas funciones nativas de Win32:
MonitorFromWindow , MonitorFromPoint y 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);
Obtenga un identificador del contexto de dispositivo de una pantalla
:
Un método genérico para recuperar el hDC de cualquier pantalla disponible.
Las coordenadas de la Pantalla o el Dispositivo de Pantalla pueden determinarse utilizando uno de los métodos descritos anteriormente cuando solo se requiere una referencia de Pantalla específica.
La propiedad
Screen.DeviceName
se puede usar como el parámetro
lpszDriver
de la función GDI +
CreateDC
.
Devolverá el hDC de la pantalla que
Graphics.FromHdc
puede usar para crear un objeto Graphics válido que permita pintar en una pantalla específica.
Aquí, asumiendo que al menos dos pantallas están disponibles:
[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);