From 3b5220dcf6c2104ee89e439d700fe377698c4d66 Mon Sep 17 00:00:00 2001
From: DismissedLight <1686188646@qq.com>
Date: Sun, 15 Oct 2023 12:59:33 +0800
Subject: [PATCH] introducing high dpi awareness controller
---
BetterGenshinImpact/AssemblyInfo.cs | 11 +-
.../DpiAwareness/DpiAwarenessController.cs | 130 ++++++++++++++++++
.../DpiAwareness/DpiAwarenessExtension.cs | 11 ++
BetterGenshinImpact/View/MainWindow.xaml.cs | 5 +-
BetterGenshinImpact/View/MaskWindow.xaml.cs | 7 +-
BetterGenshinImpact/View/PickerWindow.xaml.cs | 5 +-
6 files changed, 153 insertions(+), 16 deletions(-)
create mode 100644 BetterGenshinImpact/Helpers/DpiAwareness/DpiAwarenessController.cs
create mode 100644 BetterGenshinImpact/Helpers/DpiAwareness/DpiAwarenessExtension.cs
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;
}