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
}