简体   繁体   中英

How to move a window to another screen with a different scale?

I have these two screens:

屏幕布局

  1. 1080p, 100% scale.
    1. Bound: {3840,0,1920,1080}
    2. Native bounds: {3840,0,1920,1080}
  2. 4k, 200% scale, set as primary.
    1. Bounds: {0,0,1920,1080}
    2. Native bounds: {0,0,3840,2160}

Now, let's suppose that a multi-DPI aware app is displaying a window inside screen 2 .

How to move that window to the top-left corner of the screen 1 ?

I don't want to limit to this configuration, since the user can add, remove, or reposition screens.

目标位置


I understand that setting the Left and Top properties of a window is relative to the scaling of the window.

Setting Left = 2000; will translate to different positions depending on the scale of the window.

I already tried to set position to bounds of target screen, taking into consideration the difference in scale.

Left = nextScreen.NativeBounds.Left / (this.Scale() / nextScreen.Scale) + 30; 
Top = nextScreen.NativeBounds.Top / (this.Scale() / nextScreen.Scale) + 30;

For a window inside a 200% scale screen, the calculated Left of 1950 would translate into beyond 3840.

But it does not work. It positions the window in the middle-top position of the screen 2 .

I don't see any other way of doing this without calculating the position based on the set of screens around, which can be complex if there's many of them.

I was able to move the window with a simple p/invoke to SetWindowPos , passing the native bounds of the target 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);
    }
}

Usage:

//targetMonitor is a custom class which holds the details about a screen,
//such as bounds, name, scale, etc.

this.MoveToScreen(targetMonitor);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM