diff --git a/src/modules/Workspaces/WorkspacesEditor/OverlayWindow.xaml.cs b/src/modules/Workspaces/WorkspacesEditor/OverlayWindow.xaml.cs index 54e892d9bd..d1646e7282 100644 --- a/src/modules/Workspaces/WorkspacesEditor/OverlayWindow.xaml.cs +++ b/src/modules/Workspaces/WorkspacesEditor/OverlayWindow.xaml.cs @@ -2,8 +2,11 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Windows; +using WorkspacesEditor.Utils; + namespace WorkspacesEditor { /// @@ -11,9 +14,40 @@ namespace WorkspacesEditor /// public partial class OverlayWindow : Window { + private int _targetX; + private int _targetY; + private int _targetWidth; + private int _targetHeight; + public OverlayWindow() { InitializeComponent(); + SourceInitialized += OnWindowSourceInitialized; + } + + /// + /// Sets the target bounds for the overlay window. + /// The window will be positioned using DPI-unaware context after initialization. + /// + public void SetTargetBounds(int x, int y, int width, int height) + { + _targetX = x; + _targetY = y; + _targetWidth = width; + _targetHeight = height; + + // Set initial WPF properties (will be corrected after HWND creation) + Left = x; + Top = y; + Width = width; + Height = height; + } + + private void OnWindowSourceInitialized(object sender, EventArgs e) + { + // Reposition window using DPI-unaware context to match the virtual coordinates. + // This fixes overlay positioning on mixed-DPI multi-monitor setups. + NativeMethods.SetWindowPositionDpiUnaware(this, _targetX, _targetY, _targetWidth, _targetHeight); } } } diff --git a/src/modules/Workspaces/WorkspacesEditor/Utils/NativeMethods.cs b/src/modules/Workspaces/WorkspacesEditor/Utils/NativeMethods.cs index 4105cbe959..9687aeac63 100644 --- a/src/modules/Workspaces/WorkspacesEditor/Utils/NativeMethods.cs +++ b/src/modules/Workspaces/WorkspacesEditor/Utils/NativeMethods.cs @@ -4,6 +4,8 @@ using System; using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Interop; namespace WorkspacesEditor.Utils { @@ -17,6 +19,39 @@ namespace WorkspacesEditor.Utils [return: MarshalAs(UnmanagedType.Bool)] public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); + [DllImport("user32.dll", SetLastError = true)] + private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + + [DllImport("user32.dll")] + private static extern IntPtr SetThreadDpiAwarenessContext(IntPtr dpiContext); + + private const uint SWP_NOZORDER = 0x0004; + private const uint SWP_NOACTIVATE = 0x0010; + + private static readonly IntPtr DPI_AWARENESS_CONTEXT_UNAWARE = new IntPtr(-1); + + /// + /// Positions a WPF window using DPI-unaware context to match the virtual coordinates. + /// This fixes overlay positioning on mixed-DPI multi-monitor setups. + /// + public static void SetWindowPositionDpiUnaware(Window window, int x, int y, int width, int height) + { + var helper = new WindowInteropHelper(window).Handle; + if (helper != IntPtr.Zero) + { + // Temporarily switch to DPI-unaware context to position window. + IntPtr oldContext = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE); + try + { + SetWindowPos(helper, IntPtr.Zero, x, y, width, height, SWP_NOZORDER | SWP_NOACTIVATE); + } + finally + { + SetThreadDpiAwarenessContext(oldContext); + } + } + } + [DllImport("USER32.DLL")] public static extern bool SetForegroundWindow(IntPtr hWnd); diff --git a/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs b/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs index 9c76c26fa0..5741fd65ab 100644 --- a/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs +++ b/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs @@ -495,10 +495,10 @@ namespace WorkspacesEditor.ViewModels { var bounds = screen.Bounds; OverlayWindow overlayWindow = new OverlayWindow(); - overlayWindow.Top = bounds.Top; - overlayWindow.Left = bounds.Left; - overlayWindow.Width = bounds.Width; - overlayWindow.Height = bounds.Height; + + // Use DPI-unaware positioning to fix overlay on mixed-DPI multi-monitor setups + overlayWindow.SetTargetBounds(bounds.Left, bounds.Top, bounds.Width, bounds.Height); + overlayWindow.ShowActivated = true; overlayWindow.Topmost = true; overlayWindow.Show();