introducing high dpi awareness controller

This commit is contained in:
DismissedLight 2023-10-15 12:59:33 +08:00
parent e4cb3e78e9
commit 3b5220dcf6
6 changed files with 153 additions and 16 deletions

View File

@ -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)]

View File

@ -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;
/// <summary>
/// 高分辨率适配器
/// </summary>
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();
}
/// <summary>
/// 构造一个新的高分辨率适配器
/// </summary>
/// <param name="window">目标窗体</param>
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;
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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("测试识别窗口");
}

View File

@ -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;
}