如何将 window 移动到另一个具有不同比例的屏幕?
How to move a window to another screen with a different scale?
我有这两个屏幕:
- 1080p,100% 比例。
- 绑定:{3840,0,1920,1080}
- 原生边界:{3840,0,1920,1080}
- 4k,200% 比例,设为主要。
- 边界:{0,0,1920,1080}
- 原生边界:{0,0,3840,2160}
现在,假设一个支持多 DPI 的应用程序在 屏幕 2.
内显示 window
如何将 window 移动到 屏幕 1 的左上角?
我不想局限于此配置,因为用户可以添加、删除或重新定位屏幕。
我了解 window 的 Left
和 Top
属性的设置与 window 的缩放有关。
设置 Left = 2000;
将根据 window 的比例转换到不同的位置。
考虑到比例差异,我已经尝试将位置设置为目标屏幕的边界。
Left = nextScreen.NativeBounds.Left / (this.Scale() / nextScreen.Scale) + 30;
Top = nextScreen.NativeBounds.Top / (this.Scale() / nextScreen.Scale) + 30;
对于 200% 比例屏幕内的 window,计算得出的 Left
1950 将转化为超过 3840。
但是不行。它将 window 定位在 屏幕 2.
的中间顶部位置
如果不根据周围的屏幕集计算位置,我看不到任何其他方法可以做到这一点,如果有很多屏幕,这可能会很复杂。
我能够通过简单的 p/invoke 将 window 移动到 SetWindowPos
,传递目标 window:
internal static class Windows
{
/// <summary>
/// Special window handles.
/// </summary>
internal enum SpecialWindowHandles
{
/// <summary>
/// Places the window at the top of the Z order.
/// </summary>
Top = 0,
/// <summary>
/// Places the window at the bottom of the Z order.
/// If the hWnd parameter identifies a topmost window, the window loses its topmost status and is placed at the bottom of all other windows.
/// </summary>
Bottom = 1,
/// <summary>
/// Places the window above all non-topmost windows.
/// The window maintains its topmost position even when it is deactivated.
/// </summary>
TopMost = -1,
/// <summary>
/// Places the window above all non-topmost windows (that is, behind all topmost windows).
/// This flag has no effect if the window is already a non-topmost window.
/// </summary>
NoTopMost = -2
}
[Flags]
internal enum SetWindowPosFlags : uint
{
/// <summary>
/// If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window.
/// This prevents the calling thread from blocking its execution while other threads process the request.
/// </summary>
AsyncWindowPositioning = 0x4000,
/// <summary>
/// Prevents generation of the WM_SYNCPAINT message.
/// </summary>
DeferErase = 0x2000,
/// <summary>
/// Draws a frame (defined in the window's class description) around the window.
/// </summary>
DrawFrame = 0x0020,
/// <summary>
/// Applies new frame styles set using the SetWindowLong function.
/// Sends a WM_NCCALCSIZE message to the window, even if the window's size is not being changed.
/// If this flag is not specified, WM_NCCALCSIZE is sent only when the window's size is being changed.
/// </summary>
FrameChanged = 0x0020,
/// <summary>
/// Hides the window.
/// </summary>
HideWindow = 0x0080,
/// <summary>
/// Does not activate the window.
/// If this flag is not set, the window is activated and moved to the top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter).
/// </summary>
NoActivate = 0x0010,
/// <summary>
/// Discards the entire contents of the client area.
/// If this flag is not specified, the valid contents of the client area are saved and copied back into the client area after the window is sized or repositioned.
/// </summary>
NoCopyBits = 0x0100,
/// <summary>
/// Retains the current position (ignores X and Y parameters).
/// </summary>
NoMove = 0x0002,
/// <summary>
/// Does not change the owner window's position in the Z order.
/// </summary>
NoOwnerZOrder = 0x0200,
/// <summary>
/// Does not redraw changes. If this flag is set, no repainting of any kind occurs.
/// This applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent window uncovered as a result of the window being moved.
/// When this flag is set, the application must explicitly invalidate or redraw any parts of the window and parent window that need redrawing.
/// </summary>
NoRedraw = 0x0008,
/// <summary>
/// Same as the NoOwnerZOrder flag.
/// </summary>
NoReposition = 0x0200,
/// <summary>
/// Prevents the window from receiving the WM_WINDOWPOSCHANGING message.
/// </summary>
NoSendChanging = 0x0400,
/// <summary>
/// Retains the current size (ignores the cx and cy parameters).
/// </summary>
NoSize = 0x0001,
/// <summary>
/// Retains the current Z order (ignores the hWndInsertAfter parameter).
/// </summary>
NoZOrder = 0x0004,
/// <summary>
/// Displays the window.
/// </summary>
ShowWindow = 0x0040
}
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SetWindowPosFlags uFlags);
internal static void MoveToScreen(this Window window, Monitor next, bool fullScreen = false)
{
if (fullScreen)
{
SetWindowPos(new System.Windows.Interop.WindowInteropHelper(window).Handle, (IntPtr)SpecialWindowHandles.Top,
(int)next.NativeBounds.Left, (int)next.NativeBounds.Top, (int)next.NativeBounds.Width, (int)next.NativeBounds.Height, SetWindowPosFlags.ShowWindow);
return;
}
SetWindowPos(new System.Windows.Interop.WindowInteropHelper(window).Handle, (IntPtr)SpecialWindowHandles.Top,
(int)next.NativeBounds.Left, (int)next.NativeBounds.Top, (int)window.Width, (int)window.Height, SetWindowPosFlags.ShowWindow);
}
}
用法:
//targetMonitor is a custom class which holds the details about a screen,
//such as bounds, name, scale, etc.
this.MoveToScreen(targetMonitor);
我有这两个屏幕:
- 1080p,100% 比例。
- 绑定:{3840,0,1920,1080}
- 原生边界:{3840,0,1920,1080}
- 4k,200% 比例,设为主要。
- 边界:{0,0,1920,1080}
- 原生边界:{0,0,3840,2160}
现在,假设一个支持多 DPI 的应用程序在 屏幕 2.
内显示 window如何将 window 移动到 屏幕 1 的左上角?
我不想局限于此配置,因为用户可以添加、删除或重新定位屏幕。
我了解 window 的 Left
和 Top
属性的设置与 window 的缩放有关。
设置 Left = 2000;
将根据 window 的比例转换到不同的位置。
考虑到比例差异,我已经尝试将位置设置为目标屏幕的边界。
Left = nextScreen.NativeBounds.Left / (this.Scale() / nextScreen.Scale) + 30;
Top = nextScreen.NativeBounds.Top / (this.Scale() / nextScreen.Scale) + 30;
对于 200% 比例屏幕内的 window,计算得出的 Left
1950 将转化为超过 3840。
但是不行。它将 window 定位在 屏幕 2.
的中间顶部位置如果不根据周围的屏幕集计算位置,我看不到任何其他方法可以做到这一点,如果有很多屏幕,这可能会很复杂。
我能够通过简单的 p/invoke 将 window 移动到 SetWindowPos
,传递目标 window:
internal static class Windows
{
/// <summary>
/// Special window handles.
/// </summary>
internal enum SpecialWindowHandles
{
/// <summary>
/// Places the window at the top of the Z order.
/// </summary>
Top = 0,
/// <summary>
/// Places the window at the bottom of the Z order.
/// If the hWnd parameter identifies a topmost window, the window loses its topmost status and is placed at the bottom of all other windows.
/// </summary>
Bottom = 1,
/// <summary>
/// Places the window above all non-topmost windows.
/// The window maintains its topmost position even when it is deactivated.
/// </summary>
TopMost = -1,
/// <summary>
/// Places the window above all non-topmost windows (that is, behind all topmost windows).
/// This flag has no effect if the window is already a non-topmost window.
/// </summary>
NoTopMost = -2
}
[Flags]
internal enum SetWindowPosFlags : uint
{
/// <summary>
/// If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window.
/// This prevents the calling thread from blocking its execution while other threads process the request.
/// </summary>
AsyncWindowPositioning = 0x4000,
/// <summary>
/// Prevents generation of the WM_SYNCPAINT message.
/// </summary>
DeferErase = 0x2000,
/// <summary>
/// Draws a frame (defined in the window's class description) around the window.
/// </summary>
DrawFrame = 0x0020,
/// <summary>
/// Applies new frame styles set using the SetWindowLong function.
/// Sends a WM_NCCALCSIZE message to the window, even if the window's size is not being changed.
/// If this flag is not specified, WM_NCCALCSIZE is sent only when the window's size is being changed.
/// </summary>
FrameChanged = 0x0020,
/// <summary>
/// Hides the window.
/// </summary>
HideWindow = 0x0080,
/// <summary>
/// Does not activate the window.
/// If this flag is not set, the window is activated and moved to the top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter).
/// </summary>
NoActivate = 0x0010,
/// <summary>
/// Discards the entire contents of the client area.
/// If this flag is not specified, the valid contents of the client area are saved and copied back into the client area after the window is sized or repositioned.
/// </summary>
NoCopyBits = 0x0100,
/// <summary>
/// Retains the current position (ignores X and Y parameters).
/// </summary>
NoMove = 0x0002,
/// <summary>
/// Does not change the owner window's position in the Z order.
/// </summary>
NoOwnerZOrder = 0x0200,
/// <summary>
/// Does not redraw changes. If this flag is set, no repainting of any kind occurs.
/// This applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent window uncovered as a result of the window being moved.
/// When this flag is set, the application must explicitly invalidate or redraw any parts of the window and parent window that need redrawing.
/// </summary>
NoRedraw = 0x0008,
/// <summary>
/// Same as the NoOwnerZOrder flag.
/// </summary>
NoReposition = 0x0200,
/// <summary>
/// Prevents the window from receiving the WM_WINDOWPOSCHANGING message.
/// </summary>
NoSendChanging = 0x0400,
/// <summary>
/// Retains the current size (ignores the cx and cy parameters).
/// </summary>
NoSize = 0x0001,
/// <summary>
/// Retains the current Z order (ignores the hWndInsertAfter parameter).
/// </summary>
NoZOrder = 0x0004,
/// <summary>
/// Displays the window.
/// </summary>
ShowWindow = 0x0040
}
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SetWindowPosFlags uFlags);
internal static void MoveToScreen(this Window window, Monitor next, bool fullScreen = false)
{
if (fullScreen)
{
SetWindowPos(new System.Windows.Interop.WindowInteropHelper(window).Handle, (IntPtr)SpecialWindowHandles.Top,
(int)next.NativeBounds.Left, (int)next.NativeBounds.Top, (int)next.NativeBounds.Width, (int)next.NativeBounds.Height, SetWindowPosFlags.ShowWindow);
return;
}
SetWindowPos(new System.Windows.Interop.WindowInteropHelper(window).Handle, (IntPtr)SpecialWindowHandles.Top,
(int)next.NativeBounds.Left, (int)next.NativeBounds.Top, (int)window.Width, (int)window.Height, SetWindowPosFlags.ShowWindow);
}
}
用法:
//targetMonitor is a custom class which holds the details about a screen,
//such as bounds, name, scale, etc.
this.MoveToScreen(targetMonitor);