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;
}
}
}