diff --git a/BetterGenshinImpact/AssemblyInfo.cs b/BetterGenshinImpact/AssemblyInfo.cs index 8b5504ec..52e1961f 100644 --- a/BetterGenshinImpact/AssemblyInfo.cs +++ b/BetterGenshinImpact/AssemblyInfo.cs @@ -1,10 +1,5 @@ using System.Windows; +using System.Windows.Media; -[assembly: ThemeInfo( - ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located - //(used if a resource is not found in the page, - // or application resource dictionaries) - ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located - //(used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) -)] +[assembly: DisableDpiAwareness] +[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] \ No newline at end of file diff --git a/BetterGenshinImpact/Helpers/DpiAwareness/DpiAwarenessController.cs b/BetterGenshinImpact/Helpers/DpiAwareness/DpiAwarenessController.cs new file mode 100644 index 00000000..fafa51db --- /dev/null +++ b/BetterGenshinImpact/Helpers/DpiAwareness/DpiAwarenessController.cs @@ -0,0 +1,130 @@ +using System; +using System.Diagnostics; +using System.Windows; +using System.Windows.Interop; +using System.Windows.Media; +using Vanara.PInvoke; + +namespace BetterGenshinImpact.Helpers.DpiAwareness; + +/// +/// 高分辨率适配器 +/// +internal class DpiAwarenessController +{ + private readonly Window window; + + private HwndSource? hwndSource; + private HWND? hwnd; + private double currentDpiRatio; + + static DpiAwarenessController() + { + SHCore.SetProcessDpiAwareness(SHCore.PROCESS_DPI_AWARENESS.PROCESS_PER_MONITOR_DPI_AWARE).ThrowIfFailed(); + } + + /// + /// 构造一个新的高分辨率适配器 + /// + /// 目标窗体 + public DpiAwarenessController(Window window) + { + this.window = window; + window.Loaded += (_, _) => OnAttached(); + window.Closing += (_, _) => OnDetaching(); + } + + private void OnAttached() + { + if (window.IsInitialized) + { + AddHwndHook(); + } + else + { + window.SourceInitialized += AssociatedWindowSourceInitialized; + } + } + + private void OnDetaching() + { + RemoveHwndHook(); + } + + private void AddHwndHook() + { + hwndSource = PresentationSource.FromVisual(window) as HwndSource; + hwndSource?.AddHook(HwndHook); + hwnd = new WindowInteropHelper(window).Handle; + } + + private void RemoveHwndHook() + { + window.SourceInitialized -= AssociatedWindowSourceInitialized; + hwndSource?.RemoveHook(HwndHook); + hwnd = null; + } + + private void AssociatedWindowSourceInitialized(object? sender, EventArgs e) + { + AddHwndHook(); + + currentDpiRatio = GetScaleRatio(window); + UpdateDpiScaling(currentDpiRatio, true); + } + + private unsafe nint HwndHook(nint hWnd, int message, nint wParam, nint lParam, ref bool handled) + { + if (message is 0x02E0) + { + RECT rect = *(RECT*)&lParam; + + User32.SetWindowPosFlags flag = + User32.SetWindowPosFlags.SWP_NOZORDER + | User32.SetWindowPosFlags.SWP_NOACTIVATE + | User32.SetWindowPosFlags.SWP_NOOWNERZORDER; + User32.SetWindowPos(hWnd, default, rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top, flag); + + // we modified this fragment to correct the wrong behaviour + double newDpiRatio = GetScaleRatio(window) * currentDpiRatio; + if (newDpiRatio != currentDpiRatio) + { + UpdateDpiScaling(newDpiRatio); + } + } + + return default; + } + + private void UpdateDpiScaling(double newDpiRatio, bool useSacleCenter = false) + { + currentDpiRatio = newDpiRatio; + Debug.WriteLine($"Set dpi scaling to {currentDpiRatio:p2}"); + FrameworkElement firstChild = (FrameworkElement)VisualTreeHelper.GetChild(window, 0); + ScaleTransform transform; + if (useSacleCenter) + { + double centerX = window.Left + (window.Width / 2); + double centerY = window.Top + (window.Height / 2); + + transform = new ScaleTransform(currentDpiRatio, currentDpiRatio, centerX, centerY); + } + else + { + transform = new ScaleTransform(currentDpiRatio, currentDpiRatio); + } + + firstChild.LayoutTransform = transform; + } + private static double GetScaleRatio(Window window) + { + PresentationSource hwndSource = PresentationSource.FromVisual(window); + + // TODO: verify use hwndSource there + double wpfDpi = 96.0 * hwndSource.CompositionTarget.TransformToDevice.M11; + + HMONITOR hMonitor = User32.MonitorFromWindow(((HwndSource)hwndSource).Handle, User32.MonitorFlags.MONITOR_DEFAULTTONEAREST); + _ = SHCore.GetDpiForMonitor(hMonitor, SHCore.MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out uint dpiX, out uint _); + return dpiX / wpfDpi; + } +} \ No newline at end of file diff --git a/BetterGenshinImpact/Helpers/DpiAwareness/DpiAwarenessExtension.cs b/BetterGenshinImpact/Helpers/DpiAwareness/DpiAwarenessExtension.cs new file mode 100644 index 00000000..bc804233 --- /dev/null +++ b/BetterGenshinImpact/Helpers/DpiAwareness/DpiAwarenessExtension.cs @@ -0,0 +1,11 @@ +using System.Windows; + +namespace BetterGenshinImpact.Helpers.DpiAwareness; + +internal static class DpiAwarenessExtension +{ + public static void InitializeDpiAwareness(this Window window) + { + _ = new DpiAwarenessController(window); + } +} \ No newline at end of file diff --git a/BetterGenshinImpact/View/MainWindow.xaml.cs b/BetterGenshinImpact/View/MainWindow.xaml.cs index ecb548d8..debe1d30 100644 --- a/BetterGenshinImpact/View/MainWindow.xaml.cs +++ b/BetterGenshinImpact/View/MainWindow.xaml.cs @@ -1,6 +1,6 @@ -using System; +using BetterGenshinImpact.Helpers.DpiAwareness; using BetterGenshinImpact.ViewModel; -using System.Windows.Navigation; +using System; using Wpf.Ui; using Wpf.Ui.Controls; @@ -18,6 +18,7 @@ public partial class MainWindow : INavigationWindow DataContext = ViewModel = viewModel; InitializeComponent(); + this.InitializeDpiAwareness(); SetPageService(pageService); navigationService.SetNavigationControl(RootNavigation); diff --git a/BetterGenshinImpact/View/MaskWindow.xaml.cs b/BetterGenshinImpact/View/MaskWindow.xaml.cs index 5d27eb2e..57088cd5 100644 --- a/BetterGenshinImpact/View/MaskWindow.xaml.cs +++ b/BetterGenshinImpact/View/MaskWindow.xaml.cs @@ -1,19 +1,18 @@ using BetterGenshinImpact.Core.Recognition.OpenCv; using BetterGenshinImpact.GameTask; using BetterGenshinImpact.Helpers; +using BetterGenshinImpact.Helpers.DpiAwareness; using BetterGenshinImpact.View.Drawable; -using CommunityToolkit.Mvvm.Messaging.Messages; using CommunityToolkit.Mvvm.Messaging; +using CommunityToolkit.Mvvm.Messaging.Messages; using System; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Windows; using System.Windows.Controls; -using System.Windows.Controls.Primitives; using System.Windows.Documents; using System.Windows.Media; -using System.Windows.Shapes; using System.Windows.Threading; using Vanara.PInvoke; using FontFamily = System.Windows.Media.FontFamily; @@ -78,6 +77,8 @@ namespace BetterGenshinImpact.View _maskWindow = this; this.SetResourceReference(StyleProperty, typeof(MaskWindow)); InitializeComponent(); + this.InitializeDpiAwareness(); + LogTextBox.TextChanged += LogTextBoxTextChanged; //AddAreaSettingsControl("测试识别窗口"); } diff --git a/BetterGenshinImpact/View/PickerWindow.xaml.cs b/BetterGenshinImpact/View/PickerWindow.xaml.cs index 6c3b6c01..28171a67 100644 --- a/BetterGenshinImpact/View/PickerWindow.xaml.cs +++ b/BetterGenshinImpact/View/PickerWindow.xaml.cs @@ -1,9 +1,7 @@ -using OpenCvSharp.Internal; +using BetterGenshinImpact.Helpers.DpiAwareness; using System; -using System.ComponentModel; using System.Diagnostics; using System.Linq; -using System.Runtime.InteropServices; using System.Text; using System.Windows; using System.Windows.Input; @@ -22,6 +20,7 @@ namespace BetterGenshinImpact.View public PickerWindow() { InitializeComponent(); + this.InitializeDpiAwareness(); Loaded += OnLoaded; }