WindowExtensions.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Runtime.InteropServices; 6 7 using ManagedCommon; 8 using Microsoft.UI.Xaml; 9 using Windows.Win32; 10 using Windows.Win32.Foundation; 11 using Windows.Win32.Graphics.Gdi; 12 using Windows.Win32.UI.WindowsAndMessaging; 13 using WinUIEx; 14 15 namespace Peek.UI.Extensions 16 { 17 public static class WindowExtensions 18 { 19 public static double GetMonitorScale(this Window window) 20 { 21 var hwnd = new HWND(window.GetWindowHandle()); 22 return hwnd.GetMonitorScale(); 23 } 24 25 internal static void CenterOnMonitor(this Window window, HWND hwndDesktop, double? width = null, double? height = null) 26 { 27 var hwndToCenter = new HWND(window.GetWindowHandle()); 28 29 // If the window is maximized, restore to normal state before change its size 30 var placement = default(WINDOWPLACEMENT); 31 if (PInvoke_PeekUI.GetWindowPlacement(hwndToCenter, ref placement)) 32 { 33 if (placement.showCmd == SHOW_WINDOW_CMD.SW_MAXIMIZE) 34 { 35 placement.showCmd = SHOW_WINDOW_CMD.SW_SHOWNORMAL; 36 if (!PInvoke_PeekUI.SetWindowPlacement(hwndToCenter, in placement)) 37 { 38 Logger.LogError($"SetWindowPlacement failed with error {Marshal.GetLastWin32Error()}"); 39 } 40 } 41 } 42 else 43 { 44 Logger.LogError($"GetWindowPlacement failed with error {Marshal.GetLastWin32Error()}"); 45 } 46 47 var monitor = PInvoke_PeekUI.MonitorFromWindow(hwndDesktop, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST); 48 MONITORINFO info = default(MONITORINFO); 49 info.cbSize = 40; 50 PInvoke_PeekUI.GetMonitorInfo(monitor, ref info); 51 var dpi = PInvoke_PeekUI.GetDpiForWindow(new HWND((nint)hwndDesktop)); 52 PInvoke_PeekUI.GetWindowRect(hwndToCenter, out RECT windowRect); 53 var scalingFactor = dpi / 96d; 54 var w = width.HasValue ? (int)(width * scalingFactor) : windowRect.right - windowRect.left; 55 var h = height.HasValue ? (int)(height * scalingFactor) : windowRect.bottom - windowRect.top; 56 var cx = (info.rcMonitor.left + info.rcMonitor.right) / 2; 57 var cy = (info.rcMonitor.bottom + info.rcMonitor.top) / 2; 58 var left = cx - (w / 2); 59 var top = cy - (h / 2); 60 61 SetWindowPosOrThrow(hwndToCenter, default(HWND), left, top, w, h, SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE); 62 } 63 64 private static void SetWindowPosOrThrow(HWND hWnd, HWND hWndInsertAfter, int x, int y, int cx, int cy, SET_WINDOW_POS_FLAGS uFlags) 65 { 66 bool result = PInvoke_PeekUI.SetWindowPos(hWnd, hWndInsertAfter, x, y, cx, cy, uFlags); 67 if (!result) 68 { 69 Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error()); 70 } 71 } 72 } 73 }