Styles.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.Specialized;
  5. using Avalonia.Collections;
  6. using Avalonia.Controls;
  7. using Avalonia.PropertyStore;
  8. namespace Avalonia.Styling
  9. {
  10. /// <summary>
  11. /// A style that consists of a number of child styles.
  12. /// </summary>
  13. public class Styles : AvaloniaObject,
  14. IAvaloniaList<IStyle>,
  15. IStyle,
  16. IResourceProvider
  17. {
  18. private readonly AvaloniaList<IStyle> _styles = new();
  19. private IResourceHost? _owner;
  20. private IResourceDictionary? _resources;
  21. public Styles()
  22. {
  23. _styles.ResetBehavior = ResetBehavior.Remove;
  24. _styles.CollectionChanged += OnCollectionChanged;
  25. _styles.Validate = i =>
  26. {
  27. if (i is ControlTheme)
  28. throw new InvalidOperationException("ControlThemes cannot be added to a Styles collection.");
  29. };
  30. }
  31. public Styles(IResourceHost owner)
  32. : this()
  33. {
  34. Owner = owner;
  35. }
  36. public event NotifyCollectionChangedEventHandler? CollectionChanged;
  37. public event EventHandler? OwnerChanged;
  38. public int Count => _styles.Count;
  39. public IResourceHost? Owner
  40. {
  41. get => _owner;
  42. private set
  43. {
  44. if (_owner != value)
  45. {
  46. _owner = value;
  47. OwnerChanged?.Invoke(this, EventArgs.Empty);
  48. }
  49. }
  50. }
  51. /// <summary>
  52. /// Gets or sets a dictionary of style resources.
  53. /// </summary>
  54. public IResourceDictionary Resources
  55. {
  56. get => _resources ?? (Resources = new ResourceDictionary());
  57. set
  58. {
  59. value = value ?? throw new ArgumentNullException(nameof(Resources));
  60. var currentOwner = Owner;
  61. if (currentOwner is not null)
  62. {
  63. _resources?.RemoveOwner(currentOwner);
  64. }
  65. _resources = value;
  66. if (currentOwner is not null)
  67. {
  68. _resources.AddOwner(currentOwner);
  69. }
  70. }
  71. }
  72. bool ICollection<IStyle>.IsReadOnly => false;
  73. bool IResourceNode.HasResources
  74. {
  75. get
  76. {
  77. if (_resources?.Count > 0)
  78. {
  79. return true;
  80. }
  81. foreach (var i in this)
  82. {
  83. if (i is IResourceProvider { HasResources: true })
  84. {
  85. return true;
  86. }
  87. }
  88. return false;
  89. }
  90. }
  91. IStyle IReadOnlyList<IStyle>.this[int index] => _styles[index];
  92. IReadOnlyList<IStyle> IStyle.Children => this;
  93. public IStyle this[int index]
  94. {
  95. get => _styles[index];
  96. set => _styles[index] = value;
  97. }
  98. /// <inheritdoc/>
  99. public bool TryGetResource(object key, out object? value)
  100. {
  101. if (_resources != null && _resources.TryGetResource(key, out value))
  102. {
  103. return true;
  104. }
  105. for (var i = Count - 1; i >= 0; --i)
  106. {
  107. if (this[i].TryGetResource(key, out value))
  108. {
  109. return true;
  110. }
  111. }
  112. value = null;
  113. return false;
  114. }
  115. /// <inheritdoc/>
  116. public void AddRange(IEnumerable<IStyle> items) => _styles.AddRange(items);
  117. /// <inheritdoc/>
  118. public void InsertRange(int index, IEnumerable<IStyle> items) => _styles.InsertRange(index, items);
  119. /// <inheritdoc/>
  120. public void Move(int oldIndex, int newIndex) => _styles.Move(oldIndex, newIndex);
  121. /// <inheritdoc/>
  122. public void MoveRange(int oldIndex, int count, int newIndex) => _styles.MoveRange(oldIndex, count, newIndex);
  123. /// <inheritdoc/>
  124. public void RemoveAll(IEnumerable<IStyle> items) => _styles.RemoveAll(items);
  125. /// <inheritdoc/>
  126. public void RemoveRange(int index, int count) => _styles.RemoveRange(index, count);
  127. /// <inheritdoc/>
  128. public int IndexOf(IStyle item) => _styles.IndexOf(item);
  129. /// <inheritdoc/>
  130. public void Insert(int index, IStyle item) => _styles.Insert(index, item);
  131. /// <inheritdoc/>
  132. public void RemoveAt(int index) => _styles.RemoveAt(index);
  133. /// <inheritdoc/>
  134. public void Add(IStyle item) => _styles.Add(item);
  135. /// <inheritdoc/>
  136. public void Clear() => _styles.Clear();
  137. /// <inheritdoc/>
  138. public bool Contains(IStyle item) => _styles.Contains(item);
  139. /// <inheritdoc/>
  140. public void CopyTo(IStyle[] array, int arrayIndex) => _styles.CopyTo(array, arrayIndex);
  141. /// <inheritdoc/>
  142. public bool Remove(IStyle item) => _styles.Remove(item);
  143. public AvaloniaList<IStyle>.Enumerator GetEnumerator() => _styles.GetEnumerator();
  144. /// <inheritdoc/>
  145. IEnumerator<IStyle> IEnumerable<IStyle>.GetEnumerator() => _styles.GetEnumerator();
  146. /// <inheritdoc/>
  147. IEnumerator IEnumerable.GetEnumerator() => _styles.GetEnumerator();
  148. /// <inheritdoc/>
  149. void IResourceProvider.AddOwner(IResourceHost owner)
  150. {
  151. owner = owner ?? throw new ArgumentNullException(nameof(owner));
  152. if (Owner is not null)
  153. {
  154. throw new InvalidOperationException("The Styles already has a owner.");
  155. }
  156. Owner = owner;
  157. _resources?.AddOwner(owner);
  158. foreach (var child in this)
  159. {
  160. if (child is IResourceProvider r)
  161. {
  162. r.AddOwner(owner);
  163. }
  164. }
  165. }
  166. /// <inheritdoc/>
  167. void IResourceProvider.RemoveOwner(IResourceHost owner)
  168. {
  169. owner = owner ?? throw new ArgumentNullException(nameof(owner));
  170. if (Owner == owner)
  171. {
  172. Owner = null;
  173. _resources?.RemoveOwner(owner);
  174. foreach (var child in this)
  175. {
  176. if (child is IResourceProvider r)
  177. {
  178. r.RemoveOwner(owner);
  179. }
  180. }
  181. }
  182. }
  183. internal SelectorMatchResult TryAttach(IStyleable target, object? host)
  184. {
  185. var result = SelectorMatchResult.NeverThisType;
  186. foreach (var s in this)
  187. {
  188. if (s is not Style style)
  189. continue;
  190. var r = style.TryAttach(target, host, FrameType.Style);
  191. if (r > result)
  192. result = r;
  193. }
  194. return result;
  195. }
  196. private static IReadOnlyList<T> ToReadOnlyList<T>(ICollection list)
  197. {
  198. if (list is IReadOnlyList<T> readOnlyList)
  199. {
  200. return readOnlyList;
  201. }
  202. var result = new T[list.Count];
  203. list.CopyTo(result, 0);
  204. return result;
  205. }
  206. private static void InternalAdd(IList items, IResourceHost? owner)
  207. {
  208. if (owner is not null)
  209. {
  210. for (var i = 0; i < items.Count; ++i)
  211. {
  212. if (items[i] is IResourceProvider provider)
  213. {
  214. provider.AddOwner(owner);
  215. }
  216. }
  217. (owner as IStyleHost)?.StylesAdded(ToReadOnlyList<IStyle>(items));
  218. }
  219. }
  220. private static void InternalRemove(IList items, IResourceHost? owner)
  221. {
  222. if (owner is not null)
  223. {
  224. for (var i = 0; i < items.Count; ++i)
  225. {
  226. if (items[i] is IResourceProvider provider)
  227. {
  228. provider.RemoveOwner(owner);
  229. }
  230. }
  231. (owner as IStyleHost)?.StylesRemoved(ToReadOnlyList<IStyle>(items));
  232. }
  233. }
  234. private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
  235. {
  236. if (e.Action == NotifyCollectionChangedAction.Reset)
  237. {
  238. throw new InvalidOperationException("Reset should not be called on Styles.");
  239. }
  240. var currentOwner = Owner;
  241. switch (e.Action)
  242. {
  243. case NotifyCollectionChangedAction.Add:
  244. InternalAdd(e.NewItems!, currentOwner);
  245. break;
  246. case NotifyCollectionChangedAction.Remove:
  247. InternalRemove(e.OldItems!, currentOwner);
  248. break;
  249. case NotifyCollectionChangedAction.Replace:
  250. InternalRemove(e.OldItems!, currentOwner);
  251. InternalAdd(e.NewItems!, currentOwner);
  252. break;
  253. }
  254. CollectionChanged?.Invoke(this, e);
  255. }
  256. }
  257. }