Theme.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Windows;
  5. using System.Windows.Media;
  6. using HandyControl.Data;
  7. using HandyControl.Tools;
  8. using HandyControl.Tools.Helper;
  9. using HandyControl.Tools.Interop;
  10. using Microsoft.Win32;
  11. namespace HandyControl.Themes;
  12. public class Theme : ResourceDictionary
  13. {
  14. public Theme()
  15. {
  16. if (DesignerHelper.IsInDesignMode)
  17. {
  18. MergedDictionaries.Add(ResourceHelper.GetSkin(SkinType.Default));
  19. MergedDictionaries.Add(ResourceHelper.GetTheme());
  20. }
  21. else
  22. {
  23. InitResource();
  24. }
  25. }
  26. #region Skin
  27. private SkinType _manualSkinType;
  28. private SkinType _skin;
  29. public virtual SkinType Skin
  30. {
  31. get => _skin;
  32. set
  33. {
  34. if (_skin == value) return;
  35. _skin = value;
  36. UpdateSkin();
  37. }
  38. }
  39. public static readonly DependencyProperty SkinProperty = DependencyProperty.RegisterAttached(
  40. "Skin", typeof(SkinType), typeof(Theme), new PropertyMetadata(default(SkinType), OnSkinChanged));
  41. private static void OnSkinChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  42. {
  43. if (d is not FrameworkElement element)
  44. {
  45. return;
  46. }
  47. var skin = (SkinType) e.NewValue;
  48. var themes = new List<Theme>();
  49. GetAllThemes(element.Resources, ref themes);
  50. if (themes.Count > 0)
  51. {
  52. foreach (var theme in themes)
  53. {
  54. theme.Skin = skin;
  55. }
  56. }
  57. else
  58. {
  59. element.Resources.MergedDictionaries.Add(new Theme
  60. {
  61. Skin = skin
  62. });
  63. }
  64. }
  65. private static void GetAllThemes(ResourceDictionary resourceDictionary, ref List<Theme> themes)
  66. {
  67. if (resourceDictionary is Theme theme)
  68. {
  69. themes.Add(theme);
  70. }
  71. // we must consider it's MergedDictionaries
  72. foreach (var dictionaryMergedDictionary in resourceDictionary.MergedDictionaries)
  73. {
  74. GetAllThemes(dictionaryMergedDictionary, ref themes);
  75. }
  76. }
  77. public static void SetSkin(DependencyObject element, SkinType value)
  78. => element.SetValue(SkinProperty, value);
  79. public static SkinType GetSkin(DependencyObject element)
  80. => (SkinType) element.GetValue(SkinProperty);
  81. #endregion
  82. #region SyncWithSystem
  83. private bool _syncWithSystem;
  84. public bool SyncWithSystem
  85. {
  86. get => _syncWithSystem;
  87. set
  88. {
  89. _syncWithSystem = value;
  90. if (value)
  91. {
  92. _manualSkinType = _skin;
  93. SyncWithSystemTheme();
  94. SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged;
  95. }
  96. else
  97. {
  98. SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged;
  99. _skin = _manualSkinType;
  100. UpdateSkin();
  101. }
  102. }
  103. }
  104. private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
  105. {
  106. if (e.Category == UserPreferenceCategory.General)
  107. {
  108. SyncWithSystemTheme();
  109. }
  110. }
  111. private void SyncWithSystemTheme()
  112. {
  113. _skin = SystemHelper.DetermineIfInLightThemeMode() ? SkinType.Default : SkinType.Dark;
  114. UpdateSkin();
  115. }
  116. #endregion
  117. #region AccentColor
  118. private Color? _accentColor;
  119. public Color? AccentColor
  120. {
  121. get => _accentColor;
  122. set
  123. {
  124. _accentColor = value;
  125. if (value == null)
  126. {
  127. _precSkin = null;
  128. }
  129. UpdateSkin();
  130. }
  131. }
  132. #endregion
  133. #region Source
  134. private Uri _source;
  135. public new Uri Source
  136. {
  137. get => DesignerHelper.IsInDesignMode ? null : _source;
  138. set => _source = value;
  139. }
  140. #endregion
  141. public string Name { get; set; }
  142. private SkinType _prevSkinType;
  143. private ResourceDictionary _precSkin;
  144. public virtual ResourceDictionary GetSkin(SkinType skinType)
  145. {
  146. if (_precSkin == null || _prevSkinType != skinType)
  147. {
  148. _precSkin = ResourceHelper.GetSkin(skinType);
  149. _prevSkinType = skinType;
  150. }
  151. if (!SyncWithSystem)
  152. {
  153. if (AccentColor != null)
  154. {
  155. UpdateAccentColor(AccentColor.Value);
  156. }
  157. }
  158. else
  159. {
  160. InteropMethods.DwmGetColorizationColor(out var color, out _);
  161. UpdateAccentColor(ColorHelper.ToColor(color));
  162. }
  163. return _precSkin;
  164. }
  165. public static Theme GetTheme(string name, ResourceDictionary resourceDictionary)
  166. {
  167. if (string.IsNullOrEmpty(name) || resourceDictionary == null)
  168. {
  169. return null;
  170. }
  171. return resourceDictionary.MergedDictionaries.OfType<Theme>().FirstOrDefault(item => Equals(item.Name, name));
  172. }
  173. public virtual ResourceDictionary GetTheme() => ResourceHelper.GetTheme();
  174. private void InitResource()
  175. {
  176. if (DesignerHelper.IsInDesignMode)
  177. {
  178. return;
  179. }
  180. MergedDictionaries.Clear();
  181. MergedDictionaries.Add(GetSkin(Skin));
  182. MergedDictionaries.Add(GetTheme());
  183. }
  184. private void UpdateAccentColor(Color color)
  185. {
  186. _precSkin[ResourceToken.PrimaryColor] = color;
  187. _precSkin[ResourceToken.DarkPrimaryColor] = color;
  188. _precSkin[ResourceToken.TitleColor] = color;
  189. _precSkin[ResourceToken.SecondaryTitleColor] = color;
  190. }
  191. private void UpdateSkin() => MergedDictionaries[0] = GetSkin(Skin);
  192. }
  193. public class StandaloneTheme : Theme
  194. {
  195. public override ResourceDictionary GetTheme() => ResourceHelper.GetStandaloneTheme();
  196. }