using System; using System.Collections.Generic; using System.Windows; using System.Windows.Media; using Microsoft.Win32; namespace TeamAAS { /// /// 窗口分辨率/DPI 自适应助手。 /// public static class UiScaler { private static double _globalScale = 1.0; /// 当前全局缩放因子(弹窗读取用) public static double GlobalScale => _globalScale; /// 全局因子变化通知(弹窗订阅后自动跟随) public static event Action ScaleChanged; private static readonly Dictionary _designSizes = new Dictionary(); /// /// 主窗口:计算全局缩放因子并应用(设计基准默认 1600x900,钳制 minScale~maxScale)。 /// public static void AttachMain(Window window, double refWidth = 1600, double refHeight = 900, double minScale = 0.6, double maxScale = 2.0) { if (window == null) return; var root = window.Content as FrameworkElement; if (root == null) return; if (root.Tag is UiScalerToken) return; root.Tag = new UiScalerToken(); void Update() { double w = window.ActualWidth > 0 ? window.ActualWidth : refWidth; double h = window.ActualHeight > 0 ? window.ActualHeight : refHeight; double f = Math.Min(w / refWidth, h / refHeight); _globalScale = Math.Max(minScale, Math.Min(maxScale, f)); ApplyTransform(window, _globalScale, resizeWindow: false); ScaleChanged?.Invoke(); } window.SizeChanged += (s, e) => Update(); window.DpiChanged += (s, e) => Update(); window.Loaded += (s, e) => Update(); SystemEvents.DisplaySettingsChanged += (s, e) => window.Dispatcher.Invoke(Update); } /// /// 弹窗/子窗体:跟随全局缩放因子(只放大不缩小,窗口尺寸与内容同步缩放)。 /// 幂等:重复调用/全局钩子叠加均安全。 /// public static void Attach(Window window) { if (window == null) return; var root = window.Content as FrameworkElement; if (root == null) return; if (root.Tag is UiScalerToken) return; root.Tag = new UiScalerToken(); Action apply = () => ApplyTransform(window, _globalScale, resizeWindow: true); apply(); // 全局因子变化时跟随;关闭时退订,避免事件泄漏 ScaleChanged += apply; window.Closed += (s, e) => ScaleChanged -= apply; } private static void ApplyTransform(Window w, double f, bool resizeWindow) { var root = w.Content as FrameworkElement; if (root == null) return; // 弹窗只放大不缩小(f<1 时保持 1.0,避免小窗被缩到看不清) double effective = resizeWindow ? Math.Max(1.0, f) : f; if (Math.Abs(effective - 1.0) < 0.001) { root.LayoutTransform = null; return; } if (resizeWindow) { // 首次记录设计尺寸(XAML 声明值;NaN=自动/SizeToContent 交由内容自适应) if (!_designSizes.TryGetValue(w, out var design)) { design = new Size(w.Width, w.Height); _designSizes[w] = design; } if (!double.IsNaN(design.Width) && design.Width > 0) w.Width = design.Width * effective; if (!double.IsNaN(design.Height) && design.Height > 0) w.Height = design.Height * effective; } root.LayoutTransform = new ScaleTransform(effective, effective); } private sealed class UiScalerToken { } } }