Classes.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using Avalonia.Collections;
  7. namespace Avalonia.Controls
  8. {
  9. /// <summary>
  10. /// Holds a collection of style classes for an <see cref="IControl"/>.
  11. /// </summary>
  12. /// <remarks>
  13. /// Similar to CSS, each control may have any number of styling classes applied.
  14. /// </remarks>
  15. public class Classes : AvaloniaList<string>, IPseudoClasses
  16. {
  17. /// <summary>
  18. /// Initializes a new instance of the <see cref="Classes"/> class.
  19. /// </summary>
  20. public Classes()
  21. {
  22. }
  23. /// <summary>
  24. /// Initializes a new instance of the <see cref="Classes"/> class.
  25. /// </summary>
  26. /// <param name="items">The initial items.</param>
  27. public Classes(IEnumerable<string> items)
  28. : base(items)
  29. {
  30. }
  31. /// <summary>
  32. /// Initializes a new instance of the <see cref="Classes"/> class.
  33. /// </summary>
  34. /// <param name="items">The initial items.</param>
  35. public Classes(params string[] items)
  36. : base(items)
  37. {
  38. }
  39. /// <summary>
  40. /// Adds a style class to the collection.
  41. /// </summary>
  42. /// <param name="name">The class name.</param>
  43. /// <remarks>
  44. /// Only standard classes may be added via this method. To add pseudoclasses (classes
  45. /// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
  46. /// property.
  47. /// </remarks>
  48. public override void Add(string name)
  49. {
  50. ThrowIfPseudoclass(name, "added");
  51. if (!Contains(name))
  52. {
  53. base.Add(name);
  54. }
  55. }
  56. /// <summary>
  57. /// Adds a style classes to the collection.
  58. /// </summary>
  59. /// <param name="names">The class names.</param>
  60. /// <remarks>
  61. /// Only standard classes may be added via this method. To add pseudoclasses (classes
  62. /// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
  63. /// property.
  64. /// </remarks>
  65. public override void AddRange(IEnumerable<string> names)
  66. {
  67. var c = new List<string>();
  68. foreach (var name in names)
  69. {
  70. ThrowIfPseudoclass(name, "added");
  71. if (!Contains(name))
  72. {
  73. c.Add(name);
  74. }
  75. }
  76. base.AddRange(c);
  77. }
  78. /// <summary>
  79. /// Remvoes all non-pseudoclasses from the collection.
  80. /// </summary>
  81. public override void Clear()
  82. {
  83. for (var i = Count - 1; i >= 0; --i)
  84. {
  85. if (!this[i].StartsWith(":"))
  86. {
  87. RemoveAt(i);
  88. }
  89. }
  90. }
  91. /// <summary>
  92. /// Inserts a style class into the collection.
  93. /// </summary>
  94. /// <param name="index">The index to insert the class at.</param>
  95. /// <param name="name">The class name.</param>
  96. /// <remarks>
  97. /// Only standard classes may be added via this method. To add pseudoclasses (classes
  98. /// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
  99. /// property.
  100. /// </remarks>
  101. public override void Insert(int index, string name)
  102. {
  103. ThrowIfPseudoclass(name, "added");
  104. if (!Contains(name))
  105. {
  106. base.Insert(index, name);
  107. }
  108. }
  109. /// <summary>
  110. /// Inserts style classes into the collection.
  111. /// </summary>
  112. /// <param name="index">The index to insert the class at.</param>
  113. /// <param name="names">The class names.</param>
  114. /// <remarks>
  115. /// Only standard classes may be added via this method. To add pseudoclasses (classes
  116. /// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
  117. /// property.
  118. /// </remarks>
  119. public override void InsertRange(int index, IEnumerable<string> names)
  120. {
  121. var c = new List<string>();
  122. foreach (var name in names)
  123. {
  124. ThrowIfPseudoclass(name, "added");
  125. if (!Contains(name))
  126. {
  127. c.Add(name);
  128. }
  129. }
  130. base.InsertRange(index, c);
  131. }
  132. /// <summary>
  133. /// Removes a style class from the collection.
  134. /// </summary>
  135. /// <param name="name">The class name.</param>
  136. /// <remarks>
  137. /// Only standard classes may be removed via this method. To remove pseudoclasses (classes
  138. /// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
  139. /// property.
  140. /// </remarks>
  141. public override bool Remove(string name)
  142. {
  143. ThrowIfPseudoclass(name, "removed");
  144. return base.Remove(name);
  145. }
  146. /// <summary>
  147. /// Removes style classes from the collection.
  148. /// </summary>
  149. /// <param name="names">The class name.</param>
  150. /// <remarks>
  151. /// Only standard classes may be removed via this method. To remove pseudoclasses (classes
  152. /// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
  153. /// property.
  154. /// </remarks>
  155. public override void RemoveAll(IEnumerable<string> names)
  156. {
  157. var c = new List<string>();
  158. foreach (var name in names)
  159. {
  160. ThrowIfPseudoclass(name, "removed");
  161. if (Contains(name))
  162. {
  163. c.Add(name);
  164. }
  165. }
  166. base.RemoveAll(c);
  167. }
  168. /// <summary>
  169. /// Removes a style class from the collection.
  170. /// </summary>
  171. /// <param name="index">The index of the class in the collection.</param>
  172. /// <remarks>
  173. /// Only standard classes may be removed via this method. To remove pseudoclasses (classes
  174. /// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
  175. /// property.
  176. /// </remarks>
  177. public override void RemoveAt(int index)
  178. {
  179. var name = this[index];
  180. ThrowIfPseudoclass(name, "removed");
  181. base.RemoveAt(index);
  182. }
  183. /// <summary>
  184. /// Removes style classes from the collection.
  185. /// </summary>
  186. /// <param name="index">The first index to remove.</param>
  187. /// <param name="count">The number of items to remove.</param>
  188. public override void RemoveRange(int index, int count)
  189. {
  190. base.RemoveRange(index, count);
  191. }
  192. /// <summary>
  193. /// Removes all non-pseudoclasses in the collection and adds a new set.
  194. /// </summary>
  195. /// <param name="source">The new contents of the collection.</param>
  196. public void Replace(IList<string> source)
  197. {
  198. var toRemove = new List<string>();
  199. foreach (var name in source)
  200. {
  201. ThrowIfPseudoclass(name, "added");
  202. }
  203. foreach (var name in this)
  204. {
  205. if (!name.StartsWith(":"))
  206. {
  207. toRemove.Add(name);
  208. }
  209. }
  210. base.RemoveAll(toRemove);
  211. base.AddRange(source);
  212. }
  213. /// <inheritdoc/>
  214. void IPseudoClasses.Add(string name)
  215. {
  216. if (!Contains(name))
  217. {
  218. base.Add(name);
  219. }
  220. }
  221. /// <inheritdoc/>
  222. bool IPseudoClasses.Remove(string name)
  223. {
  224. return base.Remove(name);
  225. }
  226. private void ThrowIfPseudoclass(string name, string operation)
  227. {
  228. if (name.StartsWith(":"))
  229. {
  230. throw new ArgumentException(
  231. $"The pseudoclass '{name}' may only be {operation} by the control itself.");
  232. }
  233. }
  234. }
  235. }