using BPA.UIControl.Enums; using System; using System.Windows.Media.Animation; using System.Windows.Media; using System.Windows; using Microsoft.Win32; using BPA.UIControl.Models; using System.Linq; using System.Windows.Media.Effects; namespace BPA.UIControl { /// /// 主题管理 /// public class ThemeManager { /// /// 所有颜色 key /// public static string[] ColorKeys = new string[] { "DefaultForeground", "DefaultBackground", "Primary", "Secondary", "SecondaryForeground", "Accent", "Light", "Dark", "Border", "BorderLight", "BorderLighter", "SeconarydText", "WatermarkText", "Mask", "MaskDark", "DialogBackground", "HeaderBackground", "FloatBackground", "AllDirectionEffect", "AllDirectionEffect2", "AllDirectionEffect3", "AllDirectionEffect4", "AllDirectionEffect5", "RightTopEffect", "LeftTopEffect", "RightBottomEffect", "LeftBottomEffect", "TopEffect", "BottomEffect", "RightEffect", "LeftEffect", "ControlBackground", "ContainerBackground", }; private static bool themeApplying = false; /// /// 主题模式改变事件 /// /// 发生对象 /// 参数 public delegate void ThemeModeChangedEventHandler(object sender, ThemeModeChangedArgs e); /// /// 主题改变事件 /// public static ThemeModeChangedEventHandler ThemeModeChanged; /// /// 获取当前应用模式是否为暗色 /// /// 是否为暗色 public static bool GetIsAppDarkMode() { const string RegistryKeyPath = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"; const string RegistryValueName = "AppsUseLightTheme"; // 这里也可能是LocalMachine(HKEY_LOCAL_MACHINE) // see "https://www.addictivetips.com/windows-tips/how-to-enable-the-dark-theme-in-windows-10/" object registryValueObject = Registry.CurrentUser.OpenSubKey(RegistryKeyPath)?.GetValue(RegistryValueName); if (registryValueObject is null) return false; return (int)registryValueObject <= 0; } private static ColorAnimation GetColorAnimation(bool isDark, Color lightColor, Color darkColor) { themeApplying = true; var animation = new ColorAnimation { Duration = TimeSpan.FromMilliseconds(600), To = isDark ? darkColor : lightColor, EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut } }; animation.Completed += (sender, e) => themeApplying = false; return animation; } private static void ApplyColor(bool isDark, string colorName) { var application = Application.Current; Color lightColor; Color darkColor; if (colorName.Contains("Effect")) { lightColor = (Color)application.FindResource($"LightEffectColor"); darkColor = (Color)application.FindResource($"DarkEffectColor"); } else { lightColor = (Color)application.FindResource($"Light{colorName}Color"); darkColor = (Color)application.FindResource($"Dark{colorName}Color"); } if (application.Resources[colorName] is Brush brush) { ColorAnimation animation = GetColorAnimation(isDark, lightColor, darkColor); if (brush.IsFrozen) { brush = brush.CloneCurrentValue(); } brush.BeginAnimation(SolidColorBrush.ColorProperty, animation); application.Resources[colorName] = brush; } else if (application.Resources[colorName] is DropShadowEffect effect) { if (effect.IsFrozen) { effect = effect.CloneCurrentValue(); } effect.Color = isDark ? darkColor : lightColor; application.Resources[colorName] = effect; } } private static ThemeColor GetCurrentThemeColor(Application application) { return new ThemeColor { Primary = (Brush)application.Resources["Primary"], Light = (Brush)application.Resources["Light"], Dark = (Brush)application.Resources["Dark"], Accent = (Brush)application.Resources["Accent"], }; } private static void ApplyTheme(bool isDark) { foreach (var colorKey in ColorKeys) { ApplyColor(isDark, colorKey); } var currentThemeColor = GetCurrentThemeColor(Application.Current); var args = new ThemeModeChangedArgs(currentThemeColor, isDark); ThemeModeChanged?.Invoke(Application.Current, args); } /// /// 切换主题模式 /// /// 模式 public static void SwitchThemeMode(ThemeMode mode) { if (mode != ThemeMode.System) { SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged; } bool isDark = false; switch (mode) { case ThemeMode.Light: default: break; case ThemeMode.Dark: isDark = true; break; case ThemeMode.System: isDark = GetIsAppDarkMode(); SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged; break; } ApplyTheme(isDark); } private static void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) { if (!themeApplying) { var isDark = GetIsAppDarkMode(); ApplyTheme(isDark); } } /// /// 切换容器圆角半径 /// /// 圆角半径 public static void SwitchContainerCornerRadius(double cornerRadius) { Application.Current.Resources["AllContainerCornerRadius"] = new CornerRadius(cornerRadius); Application.Current.Resources["LeftContainerCornerRadius"] = new CornerRadius(cornerRadius, 0, 0, cornerRadius); Application.Current.Resources["RightContainerCornerRadius"] = new CornerRadius(0, cornerRadius, cornerRadius, 0); Application.Current.Resources["TopContainerCornerRadius"] = new CornerRadius(cornerRadius, cornerRadius, 0, 0); Application.Current.Resources["BottomContainerCornerRadius"] = new CornerRadius(0, 0, cornerRadius, cornerRadius); } /// /// 切换控件圆角半径 /// /// 圆角半径 public static void SwitchControlCornerRadius(double cornerRadius) { Application.Current.Resources["AllControlCornerRadius"] = new CornerRadius(cornerRadius); Application.Current.Resources["LeftControlCornerRadius"] = new CornerRadius(cornerRadius, 0, 0, cornerRadius); Application.Current.Resources["RightControlCornerRadius"] = new CornerRadius(0, cornerRadius, cornerRadius, 0); Application.Current.Resources["TopControlCornerRadius"] = new CornerRadius(cornerRadius, cornerRadius, 0, 0); Application.Current.Resources["BottomControlCornerRadius"] = new CornerRadius(0, 0, cornerRadius, cornerRadius); } private static bool CheckIsDark(Brush brush) { if (brush is SolidColorBrush color) { return (color.Color.R * 0.299 + color.Color.G * 0.578 + color.Color.B * 0.114) < 192; } return false; } /// /// 应用主题配色 /// /// 颜色配置文件路径 public static void ApplyThemeColor(string colorUrl) { var resourceDictionaries = Application.Current.Resources.MergedDictionaries; var resourceDictionary = new ResourceDictionary { Source = new Uri(colorUrl, UriKind.RelativeOrAbsolute) }; if (resourceDictionaries.Any(x => x.Source.AbsoluteUri == resourceDictionary.Source.AbsoluteUri)) { var oldColorResource = resourceDictionaries.First(x => x.Source.AbsoluteUri == resourceDictionary.Source.AbsoluteUri); resourceDictionaries.Remove(oldColorResource); } resourceDictionaries.Add(resourceDictionary); var currentBackground = (Brush)Application.Current.Resources["DefaultBackground"]; var currentThemeColor = GetCurrentThemeColor(Application.Current); var isDarkMode = CheckIsDark(currentBackground); ApplyTheme(isDarkMode); var args = new ThemeModeChangedArgs(currentThemeColor, isDarkMode); ThemeModeChanged?.Invoke(Application.Current, args); } } /// /// 主题模式改变参数 /// public class ThemeModeChangedArgs : EventArgs { /// /// 主题配色 /// public ThemeColor ThemeColor { get; set; } /// /// 是否未暗色模式 /// public bool IsDarkMode { get; set; } /// /// /// /// 主题配色 /// 是否未暗色模式 public ThemeModeChangedArgs(ThemeColor themeColor, bool isDarkMode) { ThemeColor = themeColor; IsDarkMode = isDarkMode; } } }