using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; using System.Windows.Media.Animation; namespace Sample { public enum ThemeMode { Light, Dark } /// /// 一个优雅且绝对可靠的主题管理器。 /// 它通过一个明确的资源键列表,精确地对指定画刷进行平滑的过渡动画。 /// public static class HybridThemeManager { // 【契约】我们回归到使用一个明确的、硬编码的列表。 // 这是最可靠的方式,它清晰地定义了哪些资源参与动画。 private static readonly List AnimatableBrushKeys = new List { //"AppBackgroundBrush", //"AppTextBrush" }; static HybridThemeManager() { var appResources = Application.Current.Resources; foreach (var dict in appResources.MergedDictionaries) { var keys = dict.Keys; //AnimatableBrushKeys.Clear(); AnimatableBrushKeys.AddRange( keys.OfType() .Where(key => key.EndsWith("Brush") && dict[key] is SolidColorBrush) ); } //foreach (ResourceDictionary dict in appResources) //{ //} } private static ThemeMode _currentMode = ThemeMode.Light; private static bool _isSwitching = false; public static async Task SwitchThemeAsync() { if (_isSwitching) return; _isSwitching = true; try { var targetMode = _currentMode == ThemeMode.Light ? ThemeMode.Dark : ThemeMode.Light; var appResources = Application.Current.Resources; // 步骤 1: “快照” - 根据【明确的列表】,记录当前颜色 var fromColors = TakeColorSnapshot(appResources); // 步骤 2: “交换” - 替换资源字典 SwapThemeDictionary(appResources, targetMode); // 步骤 3: “动画” - 对【明确的列表】中的画刷启动动画 await AnimateToNewTheme(appResources, fromColors); _currentMode = targetMode; } finally { _isSwitching = false; } } private static Dictionary TakeColorSnapshot(ResourceDictionary resources) { return AnimatableBrushKeys .Where(key => resources.Contains(key) && resources[key] is SolidColorBrush) .ToDictionary(key => key, key => ((SolidColorBrush)resources[key]).Color); } private static void SwapThemeDictionary(ResourceDictionary resources, ThemeMode targetMode) { var newThemeUri = new Uri($"/Themes/{targetMode}.xaml", UriKind.Relative); var newThemeDict = new ResourceDictionary { Source = newThemeUri }; var oldDict = resources.MergedDictionaries.FirstOrDefault(d => d.Source?.OriginalString.Contains("/Themes/") ?? false); if (oldDict != null) { resources.MergedDictionaries.Remove(oldDict); } resources.MergedDictionaries.Add(newThemeDict); } private static Task AnimateToNewTheme(ResourceDictionary resources, IReadOnlyDictionary fromColors) { var animationTasks = new List(); foreach (var key in AnimatableBrushKeys) { if (resources.Contains(key) && resources[key] is SolidColorBrush newBrush && fromColors.TryGetValue(key, out var fromColor)) { // 【健壮性】依然保留对 StaticResource (冻结) 的处理 if (newBrush.IsFrozen) { newBrush = newBrush.Clone(); resources[key] = newBrush; } var toColor = newBrush.Color; newBrush.Color = fromColor; // 伪装 animationTasks.Add(AnimateBrushColorAsync(newBrush, toColor)); } } return Task.WhenAll(animationTasks); } private static Task AnimateBrushColorAsync(SolidColorBrush brush, Color toColor) { var tcs = new TaskCompletionSource(); var animation = new ColorAnimation { From = brush.Color, To = toColor, Duration = TimeSpan.FromMilliseconds(400), EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut } }; animation.Completed += (s, e) => { brush.BeginAnimation(SolidColorBrush.ColorProperty, null); brush.Color = toColor; tcs.TrySetResult(true); }; brush.BeginAnimation(SolidColorBrush.ColorProperty, animation); return tcs.Task; } } }