优化
This commit is contained in:
137
Sample/HybridThemeManager.cs
Normal file
137
Sample/HybridThemeManager.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
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 }
|
||||
|
||||
/// <summary>
|
||||
/// 一个优雅且绝对可靠的主题管理器。
|
||||
/// 它通过一个明确的资源键列表,精确地对指定画刷进行平滑的过渡动画。
|
||||
/// </summary>
|
||||
public static class HybridThemeManager
|
||||
{
|
||||
// 【契约】我们回归到使用一个明确的、硬编码的列表。
|
||||
// 这是最可靠的方式,它清晰地定义了哪些资源参与动画。
|
||||
private static readonly List<string> AnimatableBrushKeys = new List<string>
|
||||
{
|
||||
//"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<string>()
|
||||
.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<string, Color> 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<string, Color> fromColors)
|
||||
{
|
||||
var animationTasks = new List<Task>();
|
||||
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<bool>();
|
||||
var animation = new ColorAnimation
|
||||
{
|
||||
From = brush.Color,
|
||||
To = toColor,
|
||||
Duration = TimeSpan.FromMilliseconds(400),
|
||||
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
|
||||
};
|
||||
|
||||
animation.Completed += (s, e) =>
|
||||
{
|
||||
brush.BeginAnimation(SolidColorBrush.ColorProperty, null);
|
||||
brush.Color = toColor;
|
||||
tcs.TrySetResult(true);
|
||||
};
|
||||
brush.BeginAnimation(SolidColorBrush.ColorProperty, animation);
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user