using System.Reflection; using System.Windows.Controls; using System.Windows.Media.Animation; using System.Windows.Shapes; using WPFluent.Controls; using WPFluent.Extensions; using WPFluent.Markup; namespace WPFluent.Appearance; /// /// 允许通过交换包含动态资源的资源字典来管理应用程序主题。 /// /// /// ThemeManager.ChangeAppTheme( ThemeType.Light ); if /// (ThemeManager.GetAppTheme() == ThemeType.Dark) { ThemeManager.ChangeAppTheme( /// ThemeType.Light ); } ThemeManager.Changed += (theme, accent) => { /// Debug.WriteLine($"Application theme changed to {theme.ToString()}"); }; /// public class ThemeManager { internal static string LibraryNamespace => Assembly.GetExecutingAssembly().GetName().Name; public static string ThemesDictionaryPath => $"pack://application:,,,/{LibraryNamespace};component/Themes/"; private static ThemeType _activeApplicationTheme = ThemeType.Unknow; public static event Action? Changed; #region Animation private static Task AnimateBrushColorAsync(SolidColorBrush brush, Color toColor, int durationMs = 500) { if (brush.IsFrozen) return Task.CompletedTask; // 冻结的Brush无法动画,直接返回完成的Task var tcs = new TaskCompletionSource(); var anim = new ColorAnimation { To = toColor, Duration = TimeSpan.FromMilliseconds(durationMs), EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut }, FillBehavior = FillBehavior.HoldEnd }; // 在动画完成时标记任务完成 anim.Completed += (s, e) => tcs.SetResult(true); brush.BeginAnimation(SolidColorBrush.ColorProperty, anim); return tcs.Task; } /// /// 带淡入淡出过渡动画的主题切换 /// private static async void ApplyThemeWithAnimation(ThemeType theme) { try { var window = Application.Current.MainWindow; if (window == null) return; // 确保内容容器支持 Children if (!(window.Content is Panel panel)) throw new InvalidOperationException("ThemeManager requires Window.Content to be a Panel."); // 创建遮罩 var overlay = new Rectangle { Fill = new SolidColorBrush(Colors.Black), Opacity = 0, IsHitTestVisible = false, }; Panel.SetZIndex(overlay, int.MaxValue); panel.Children.Add(overlay); // 淡入 await AnimateOpacity(overlay, 0, 1, TimeSpan.FromMilliseconds(300)); // 切换资源字典 //ApplyThemeInternal(theme); ChangeAppTheme(theme); // 淡出 await AnimateOpacity(overlay, 1, 0, TimeSpan.FromMilliseconds(300)); panel.Children.Remove(overlay); Changed?.Invoke(theme, AccentColorManager.SystemAccent); } catch (Exception ex) { } } private static Task AnimateOpacity(UIElement element, double from, double to, TimeSpan duration) { var tcs = new TaskCompletionSource(); var anim = new DoubleAnimation(from, to, new Duration(duration)); anim.Completed += (s, e) => tcs.SetResult(true); element.BeginAnimation(UIElement.OpacityProperty, anim); return tcs.Task; } #endregion /// /// 将资源应用于 . /// public static void ApplyResourcesToElement(FrameworkElement frameworkElement) { if (frameworkElement.Resources.MergedDictionaries.Count < ThemeManager.Current.Resources.MergedDictionaries.Count) { foreach (var dictionary in ThemeManager.Current.Resources.MergedDictionaries) { frameworkElement.Resources.MergedDictionaries.Add(dictionary); } } } /// /// 更改当前应用程序主题。 /// /// 要设置的主题 /// 是否应用自定义背景效果。 /// 是否应更改颜色强调。 public static void ChangeAppTheme(ThemeType applicationTheme, WindowBackdropType backgroundEffect = WindowBackdropType.Mica, bool updateAccent = true) { if (updateAccent) { //TODO AccentColorManager.Apply( Color.FromRgb(194, 115, 37), applicationTheme, false); } if (applicationTheme == ThemeType.Unknow) { return; } ResourceDictionaryManager appDictionaries = new(); var themeDictionaryName = applicationTheme switch { ThemeType.Light => "Light", ThemeType.Dark => "Dark", ThemeType.HcWhite => "HCWhite", ThemeType.HcBlack => "HCBlack", _ => "Light" }; //ApplyThemeWithAnimation(applicationTheme); if (!appDictionaries.UpdateDictionary( "themes", new Uri($"{ThemesDictionaryPath}{themeDictionaryName}.xaml", UriKind.Absolute))) { return; } _activeApplicationTheme = applicationTheme; Changed?.Invoke(applicationTheme, AccentColorManager.SystemAccent); if (Current.MainWindow is Window mainWindow) { WindowBackgroundManager.UpdateBackground(mainWindow, applicationTheme, backgroundEffect); } } /// /// 获取当前设置的应用程序主题。 /// /// if something goes wrong. public static ThemeType GetAppTheme() { if (_activeApplicationTheme == ThemeType.Unknow) { ResourceDictionaryManager appDictionaries = new(); var themeDictionary = appDictionaries.LookupDictionary("themes"); if (themeDictionary == null) { return ThemeType.Unknow; } var themeUri = themeDictionary.Source.ToString(); if (themeUri.Contains("light", StringComparison.OrdinalIgnoreCase)) { _activeApplicationTheme = ThemeType.Light; } if (themeUri.Contains("dark", StringComparison.OrdinalIgnoreCase)) { _activeApplicationTheme = ThemeType.Dark; } if (themeUri.Contains("hcwhite", StringComparison.OrdinalIgnoreCase)) { _activeApplicationTheme = ThemeType.HcWhite; } if (themeUri.Contains("hcblack", StringComparison.OrdinalIgnoreCase)) { _activeApplicationTheme = ThemeType.HcBlack; } } return _activeApplicationTheme; } #region UiApplication private static ThemeManager? _themeManagerInstance; private readonly Application? application; private Window? mainWindow; private ResourceDictionary? resources; /// /// 初始化 实例. /// private ThemeManager(Application? application) { if (application is null) { return; } if (!ApplicationHasResources(application)) { return; } this.application = application; System.Diagnostics.Debug .WriteLine($"INFO | {typeof(ThemeManager)} application is {this.application}", "WPFluent"); } private static bool ApplicationHasResources(Application? application) { return application .Resources.MergedDictionaries .Where(e => e.Source is not null) .Any( e => e.Source .ToString() .Contains(ThemeManager.LibraryNamespace, StringComparison.OrdinalIgnoreCase)); } /// /// 将应用程序的 变为 shutdown 模式。 /// public void Shutdown() { application?.Shutdown(); } /// /// 获取或设置应用程序的资源。 /// public object TryFindResource(object resourceKey) { return Resources[resourceKey]; } /// /// 获取当前UI应用程序。 /// public static ThemeManager Current { get { _themeManagerInstance ??= new ThemeManager(Application.Current); return _themeManagerInstance; } } /// /// 获取或设置应用程序的主窗口。 /// public Window? MainWindow { get => application?.MainWindow ?? mainWindow; set { if (application != null) { application.MainWindow = value; } mainWindow = value; } } /// /// 获取或设置应用程序的资源。 /// public ResourceDictionary Resources { get { if (resources is null) { resources = []; try { var themesDictionary = new ThemesDictionary(); var controlsDictionary = new ControlsDictionary(); resources.MergedDictionaries.Add(themesDictionary); resources.MergedDictionaries.Add(controlsDictionary); } catch { } } return application?.Resources ?? resources; } set { if (application is not null) { application.Resources = value; } resources = value; } } #endregion }