From 57b71b433c64ca3e19d1b885bd284f9f3cdeb711 Mon Sep 17 00:00:00 2001 From: Yimeng Wu Date: Sat, 3 Oct 2020 22:10:26 +0800 Subject: [PATCH] Fix issue where packaged apps don't respond to Windows theme changes (#175) --- ModernWpf/Helpers/ColorsHelper.cs | 83 ++++++++++++++------------ ModernWpf/Helpers/OSVersionHelper.cs | 12 +++- ModernWpf/Helpers/PackagedAppHelper.cs | 33 ++++++++++ ModernWpf/ThemeManager.cs | 22 +++---- ModernWpf/UISettingsResources.cs | 58 ++++++++++-------- 5 files changed, 129 insertions(+), 79 deletions(-) create mode 100644 ModernWpf/Helpers/PackagedAppHelper.cs diff --git a/ModernWpf/Helpers/ColorsHelper.cs b/ModernWpf/Helpers/ColorsHelper.cs index 94968d58..aa368386 100644 --- a/ModernWpf/Helpers/ColorsHelper.cs +++ b/ModernWpf/Helpers/ColorsHelper.cs @@ -3,12 +3,14 @@ using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Media; +using System.Windows.Threading; +using Microsoft.Win32; using ModernWpf.Media.ColorPalette; using Windows.UI.ViewManagement; namespace ModernWpf { - internal class ColorsHelper + internal class ColorsHelper : DispatcherObject { private const string AccentKey = "SystemAccentColor"; private const string AccentDark1Key = "SystemAccentColorDark1"; @@ -21,7 +23,7 @@ internal class ColorsHelper internal static readonly Color DefaultAccentColor = Color.FromRgb(0x00, 0x78, 0xD7); private readonly ResourceDictionary _colors = new ResourceDictionary(); - private object _uiSettings; + private UISettings _uiSettings; private Color _systemBackground; private Color _systemAccent; @@ -34,7 +36,7 @@ private ColorsHelper() } } - public static bool SystemColorsSupported { get; } = OSVersionHelper.IsWindows10; + public static bool SystemColorsSupported { get; } = OSVersionHelper.IsWindows10OrGreater; public static ColorsHelper Current { get; } = new ColorsHelper(); @@ -42,9 +44,11 @@ private ColorsHelper() public ApplicationTheme? SystemTheme { get; private set; } + public Color SystemAccentColor => _systemAccent; + public event EventHandler SystemThemeChanged; - public event EventHandler AccentColorChanged; + public event EventHandler SystemAccentColorChanged; [MethodImpl(MethodImplOptions.NoInlining)] public void FetchSystemAccentColors() @@ -108,48 +112,53 @@ public static void UpdateBrushes(ResourceDictionary themeDictionary, ResourceDic } [MethodImpl(MethodImplOptions.NoInlining)] - internal static Color GetSystemAccentColor() + private void ListenToSystemColorChanges() { - var uiSettings = new UISettings(); - return uiSettings.GetColorValue(UIColorType.Accent).ToColor(); + _uiSettings = new UISettings(); + _uiSettings.ColorValuesChanged += OnColorValuesChanged; + + if (PackagedAppHelper.IsPackagedApp) + { + SystemEvents.UserPreferenceChanged += OnUserPreferenceChanged; + } + + _systemBackground = _uiSettings.GetColorValue(UIColorType.Background).ToColor(); + _systemAccent = _uiSettings.GetColorValue(UIColorType.Accent).ToColor(); + UpdateSystemAppTheme(); } [MethodImpl(MethodImplOptions.NoInlining)] - private void ListenToSystemColorChanges() + private void OnColorValuesChanged(UISettings sender, object args) { - var uiSettings = new UISettings(); - - _systemBackground = uiSettings.GetColorValue(UIColorType.Background).ToColor(); - _systemAccent = uiSettings.GetColorValue(UIColorType.Accent).ToColor(); + Dispatcher.BeginInvoke(UpdateColorValues); + } - uiSettings.ColorValuesChanged += (sender, args) => + [MethodImpl(MethodImplOptions.NoInlining)] + private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + { + if (e.Category == UserPreferenceCategory.General) { -#if DEBUG - //for (int i = 0; i <= 8; i++) - //{ - // var colorType = (UIColorType)i; - // Debug.WriteLine(colorType + " = " + sender.GetColorValue(colorType)); - //} -#endif - var background = sender.GetColorValue(UIColorType.Background).ToColor(); - if (_systemBackground != background) - { - _systemBackground = background; - UpdateSystemAppTheme(); - SystemThemeChanged?.Invoke(null, EventArgs.Empty); - } - - var accent = sender.GetColorValue(UIColorType.Accent).ToColor(); - if (_systemAccent != accent) - { - _systemAccent = accent; - AccentColorChanged?.Invoke(null, EventArgs.Empty); - } - }; + UpdateColorValues(); + } + } - _uiSettings = uiSettings; + [MethodImpl(MethodImplOptions.NoInlining)] + private void UpdateColorValues() + { + var background = _uiSettings.GetColorValue(UIColorType.Background).ToColor(); + if (_systemBackground != background) + { + _systemBackground = background; + UpdateSystemAppTheme(); + SystemThemeChanged?.Invoke(null, EventArgs.Empty); + } - UpdateSystemAppTheme(); + var accent = _uiSettings.GetColorValue(UIColorType.Accent).ToColor(); + if (_systemAccent != accent) + { + _systemAccent = accent; + SystemAccentColorChanged?.Invoke(null, EventArgs.Empty); + } } private void UpdateSystemAppTheme() diff --git a/ModernWpf/Helpers/OSVersionHelper.cs b/ModernWpf/Helpers/OSVersionHelper.cs index 9cb6077a..fcfe33be 100644 --- a/ModernWpf/Helpers/OSVersionHelper.cs +++ b/ModernWpf/Helpers/OSVersionHelper.cs @@ -1,20 +1,26 @@ using System; +using System.Diagnostics; using System.Runtime.InteropServices; namespace ModernWpf { internal static class OSVersionHelper { + private static readonly Version _osVersion = GetOSVersion(); + internal static bool IsWindowsNT { get; } = Environment.OSVersion.Platform == PlatformID.Win32NT; - internal static bool IsWindows10 { get; } = IsWindowsNT && IsWindows10Impl(); + internal static bool IsWindows8OrGreater { get; } = IsWindowsNT && _osVersion >= new Version(6, 2); + + internal static bool IsWindows10OrGreater { get; } = IsWindowsNT && _osVersion >= new Version(10, 0); - private static bool IsWindows10Impl() + private static Version GetOSVersion() { var osv = new RTL_OSVERSIONINFOEX(); osv.dwOSVersionInfoSize = (uint)Marshal.SizeOf(osv); int ret = RtlGetVersion(out osv); - return ret == 0 && osv.dwMajorVersion >= 10; + Debug.Assert(ret == 0); + return new Version((int)osv.dwMajorVersion, (int)osv.dwMinorVersion, (int)osv.dwBuildNumber); } [DllImport("ntdll.dll")] diff --git a/ModernWpf/Helpers/PackagedAppHelper.cs b/ModernWpf/Helpers/PackagedAppHelper.cs new file mode 100644 index 00000000..9b6fe002 --- /dev/null +++ b/ModernWpf/Helpers/PackagedAppHelper.cs @@ -0,0 +1,33 @@ +using System.Runtime.InteropServices; +using System.Text; + +namespace ModernWpf +{ + internal static class PackagedAppHelper + { + private const long APPMODEL_ERROR_NO_PACKAGE = 15700L; + + public static bool IsPackagedApp + { + get + { + if (OSVersionHelper.IsWindows8OrGreater) + { + int length = 0; + var sb = new StringBuilder(0); + GetCurrentPackageFullName(ref length, sb); + + sb.Length = length; + int result = GetCurrentPackageFullName(ref length, sb); + + return result != APPMODEL_ERROR_NO_PACKAGE; + } + + return false; + } + } + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern int GetCurrentPackageFullName(ref int packageFullNameLength, StringBuilder packageFullName); + } +} diff --git a/ModernWpf/ThemeManager.cs b/ModernWpf/ThemeManager.cs index f0ca8f6c..2b64c4de 100644 --- a/ModernWpf/ThemeManager.cs +++ b/ModernWpf/ThemeManager.cs @@ -204,7 +204,7 @@ private void UpdateActualAccentColor() { if (UsingSystemAccentColor) { - ActualAccentColor = ColorsHelper.GetSystemAccentColor(); + ActualAccentColor = ColorsHelper.Current.SystemAccentColor; } else { @@ -786,7 +786,7 @@ internal void Initialize() appResources.MergedDictionaries.RemoveAll(); ColorsHelper.Current.SystemThemeChanged += OnSystemThemeChanged; - ColorsHelper.Current.AccentColorChanged += OnSystemAccentColorChanged; + ColorsHelper.Current.SystemAccentColorChanged += OnSystemAccentColorChanged; appResources.MergedDictionaries.Insert(0, ColorsHelper.Current.Colors); UpdateActualAccentColor(); @@ -803,24 +803,18 @@ internal void Initialize() private void OnSystemThemeChanged(object sender, EventArgs e) { - Dispatcher.BeginInvoke(() => + if (UsingSystemTheme) { - if (UsingSystemTheme) - { - UpdateActualApplicationTheme(); - } - }); + UpdateActualApplicationTheme(); + } } private void OnSystemAccentColorChanged(object sender, EventArgs e) { - Dispatcher.BeginInvoke(() => + if (UsingSystemAccentColor) { - if (UsingSystemAccentColor) - { - UpdateActualAccentColor(); - } - }); + UpdateActualAccentColor(); + } } private void OnSystemParametersChanged(object sender, PropertyChangedEventArgs e) diff --git a/ModernWpf/UISettingsResources.cs b/ModernWpf/UISettingsResources.cs index 0aa3a719..2114d974 100644 --- a/ModernWpf/UISettingsResources.cs +++ b/ModernWpf/UISettingsResources.cs @@ -1,6 +1,7 @@ using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Threading; +using Microsoft.Win32; using Windows.Foundation.Metadata; using Windows.UI.ViewManagement; @@ -11,7 +12,8 @@ internal class UISettingsResources : ResourceDictionary private const string UniversalApiContractName = "Windows.Foundation.UniversalApiContract"; private const string AutoHideScrollBarsKey = "AutoHideScrollBars"; - private object _uiSettings; + private readonly Dispatcher _dispatcher = Dispatcher.CurrentDispatcher; + private UISettings _uiSettings; public UISettingsResources() { @@ -20,7 +22,7 @@ public UISettingsResources() return; } - if (OSVersionHelper.IsWindows10) + if (OSVersionHelper.IsWindows10OrGreater) { Initialize(); } @@ -29,51 +31,56 @@ public UISettingsResources() [MethodImpl(MethodImplOptions.NoInlining)] private void Initialize() { - var uiSettings = new UISettings(); + _uiSettings = new UISettings(); if (ApiInformation.IsApiContractPresent(UniversalApiContractName, 4)) { - InitializeForContract4(uiSettings); + InitializeForContract4(); } if (ApiInformation.IsApiContractPresent(UniversalApiContractName, 8)) { - InitializeForContract8(uiSettings); + InitializeForContract8(); } - - _uiSettings = uiSettings; } [MethodImpl(MethodImplOptions.NoInlining)] - private void InitializeForContract4(UISettings settings) + private void InitializeForContract4() { - settings.AdvancedEffectsEnabledChanged += (sender, args) => + _uiSettings.AdvancedEffectsEnabledChanged += (sender, args) => { - Application.Current.Dispatcher.BeginInvoke(() => - { - ApplyAdvancedEffectsEnabled(sender.AdvancedEffectsEnabled); - }); + _dispatcher.BeginInvoke(ApplyAdvancedEffectsEnabled); }; - ApplyAdvancedEffectsEnabled(settings.AdvancedEffectsEnabled); + + if (PackagedAppHelper.IsPackagedApp) + { + SystemEvents.UserPreferenceChanged += (sender, args) => + { + if (args.Category == UserPreferenceCategory.General) + { + ApplyAdvancedEffectsEnabled(); + } + }; + } + + ApplyAdvancedEffectsEnabled(); } [MethodImpl(MethodImplOptions.NoInlining)] - private void InitializeForContract8(UISettings settings) + private void InitializeForContract8() { - settings.AutoHideScrollBarsChanged += (sender, args) => + _uiSettings.AutoHideScrollBarsChanged += (sender, args) => { - Application.Current.Dispatcher.BeginInvoke(() => - { - ApplyAutoHideScrollBars(sender.AutoHideScrollBars); - }); + _dispatcher.BeginInvoke(ApplyAutoHideScrollBars); }; - ApplyAutoHideScrollBars(settings.AutoHideScrollBars); + ApplyAutoHideScrollBars(); } - private void ApplyAdvancedEffectsEnabled(bool value) + [MethodImpl(MethodImplOptions.NoInlining)] + private void ApplyAdvancedEffectsEnabled() { var key = SystemParameters.DropShadowKey; - if (value) + if (_uiSettings.AdvancedEffectsEnabled) { Remove(key); } @@ -83,9 +90,10 @@ private void ApplyAdvancedEffectsEnabled(bool value) } } - private void ApplyAutoHideScrollBars(bool value) + [MethodImpl(MethodImplOptions.NoInlining)] + private void ApplyAutoHideScrollBars() { - this[AutoHideScrollBarsKey] = value; + this[AutoHideScrollBarsKey] = _uiSettings.AutoHideScrollBars; } } }