TemplatedControl.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. using System;
  2. using Avalonia.Controls.Documents;
  3. using Avalonia.Controls.Templates;
  4. using Avalonia.Interactivity;
  5. using Avalonia.Logging;
  6. using Avalonia.LogicalTree;
  7. using Avalonia.Media;
  8. using Avalonia.PropertyStore;
  9. using Avalonia.Styling;
  10. using Avalonia.VisualTree;
  11. namespace Avalonia.Controls.Primitives
  12. {
  13. /// <summary>
  14. /// A lookless control whose visual appearance is defined by its <see cref="Template"/>.
  15. /// </summary>
  16. public class TemplatedControl : Control
  17. {
  18. /// <summary>
  19. /// Defines the <see cref="Background"/> property.
  20. /// </summary>
  21. public static readonly StyledProperty<IBrush?> BackgroundProperty =
  22. Border.BackgroundProperty.AddOwner<TemplatedControl>();
  23. /// <summary>
  24. /// Defines the <see cref="BorderBrush"/> property.
  25. /// </summary>
  26. public static readonly StyledProperty<IBrush?> BorderBrushProperty =
  27. Border.BorderBrushProperty.AddOwner<TemplatedControl>();
  28. /// <summary>
  29. /// Defines the <see cref="BorderThickness"/> property.
  30. /// </summary>
  31. public static readonly StyledProperty<Thickness> BorderThicknessProperty =
  32. Border.BorderThicknessProperty.AddOwner<TemplatedControl>();
  33. /// <summary>
  34. /// Defines the <see cref="CornerRadius"/> property.
  35. /// </summary>
  36. public static readonly StyledProperty<CornerRadius> CornerRadiusProperty =
  37. Border.CornerRadiusProperty.AddOwner<TemplatedControl>();
  38. /// <summary>
  39. /// Defines the <see cref="FontFamily"/> property.
  40. /// </summary>
  41. public static readonly StyledProperty<FontFamily> FontFamilyProperty =
  42. TextElement.FontFamilyProperty.AddOwner<TemplatedControl>();
  43. /// <summary>
  44. /// Defines the <see cref="FontSize"/> property.
  45. /// </summary>
  46. public static readonly StyledProperty<double> FontSizeProperty =
  47. TextElement.FontSizeProperty.AddOwner<TemplatedControl>();
  48. /// <summary>
  49. /// Defines the <see cref="FontStyle"/> property.
  50. /// </summary>
  51. public static readonly StyledProperty<FontStyle> FontStyleProperty =
  52. TextElement.FontStyleProperty.AddOwner<TemplatedControl>();
  53. /// <summary>
  54. /// Defines the <see cref="FontWeight"/> property.
  55. /// </summary>
  56. public static readonly StyledProperty<FontWeight> FontWeightProperty =
  57. TextElement.FontWeightProperty.AddOwner<TemplatedControl>();
  58. /// <summary>
  59. /// Defines the <see cref="FontWeight"/> property.
  60. /// </summary>
  61. public static readonly StyledProperty<FontStretch> FontStretchProperty =
  62. TextElement.FontStretchProperty.AddOwner<TemplatedControl>();
  63. /// <summary>
  64. /// Defines the <see cref="Foreground"/> property.
  65. /// </summary>
  66. public static readonly StyledProperty<IBrush?> ForegroundProperty =
  67. TextElement.ForegroundProperty.AddOwner<TemplatedControl>();
  68. /// <summary>
  69. /// Defines the <see cref="Padding"/> property.
  70. /// </summary>
  71. public static readonly StyledProperty<Thickness> PaddingProperty =
  72. Decorator.PaddingProperty.AddOwner<TemplatedControl>();
  73. /// <summary>
  74. /// Defines the <see cref="Template"/> property.
  75. /// </summary>
  76. public static readonly StyledProperty<IControlTemplate?> TemplateProperty =
  77. AvaloniaProperty.Register<TemplatedControl, IControlTemplate?>(nameof(Template));
  78. /// <summary>
  79. /// Defines the IsTemplateFocusTarget attached property.
  80. /// </summary>
  81. public static readonly AttachedProperty<bool> IsTemplateFocusTargetProperty =
  82. AvaloniaProperty.RegisterAttached<TemplatedControl, Control, bool>("IsTemplateFocusTarget");
  83. /// <summary>
  84. /// Defines the <see cref="TemplateApplied"/> routed event.
  85. /// </summary>
  86. public static readonly RoutedEvent<TemplateAppliedEventArgs> TemplateAppliedEvent =
  87. RoutedEvent.Register<TemplatedControl, TemplateAppliedEventArgs>(
  88. nameof(TemplateApplied),
  89. RoutingStrategies.Direct);
  90. private IControlTemplate? _appliedTemplate;
  91. /// <summary>
  92. /// Initializes static members of the <see cref="TemplatedControl"/> class.
  93. /// </summary>
  94. static TemplatedControl()
  95. {
  96. ClipToBoundsProperty.OverrideDefaultValue<TemplatedControl>(true);
  97. TemplateProperty.Changed.AddClassHandler<TemplatedControl>((x, e) => x.OnTemplateChanged(e));
  98. }
  99. /// <summary>
  100. /// Raised when the control's template is applied.
  101. /// </summary>
  102. public event EventHandler<TemplateAppliedEventArgs>? TemplateApplied
  103. {
  104. add { AddHandler(TemplateAppliedEvent, value); }
  105. remove { RemoveHandler(TemplateAppliedEvent, value); }
  106. }
  107. /// <summary>
  108. /// Gets or sets the brush used to draw the control's background.
  109. /// </summary>
  110. public IBrush? Background
  111. {
  112. get { return GetValue(BackgroundProperty); }
  113. set { SetValue(BackgroundProperty, value); }
  114. }
  115. /// <summary>
  116. /// Gets or sets the brush used to draw the control's border.
  117. /// </summary>
  118. public IBrush? BorderBrush
  119. {
  120. get { return GetValue(BorderBrushProperty); }
  121. set { SetValue(BorderBrushProperty, value); }
  122. }
  123. /// <summary>
  124. /// Gets or sets the thickness of the control's border.
  125. /// </summary>
  126. public Thickness BorderThickness
  127. {
  128. get { return GetValue(BorderThicknessProperty); }
  129. set { SetValue(BorderThicknessProperty, value); }
  130. }
  131. /// <summary>
  132. /// Gets or sets the radius of the border rounded corners.
  133. /// </summary>
  134. public CornerRadius CornerRadius
  135. {
  136. get { return GetValue(CornerRadiusProperty); }
  137. set { SetValue(CornerRadiusProperty, value); }
  138. }
  139. /// <summary>
  140. /// Gets or sets the font family used to draw the control's text.
  141. /// </summary>
  142. public FontFamily FontFamily
  143. {
  144. get { return GetValue(FontFamilyProperty); }
  145. set { SetValue(FontFamilyProperty, value); }
  146. }
  147. /// <summary>
  148. /// Gets or sets the size of the control's text in points.
  149. /// </summary>
  150. public double FontSize
  151. {
  152. get { return GetValue(FontSizeProperty); }
  153. set { SetValue(FontSizeProperty, value); }
  154. }
  155. /// <summary>
  156. /// Gets or sets the font style used to draw the control's text.
  157. /// </summary>
  158. public FontStyle FontStyle
  159. {
  160. get { return GetValue(FontStyleProperty); }
  161. set { SetValue(FontStyleProperty, value); }
  162. }
  163. /// <summary>
  164. /// Gets or sets the font weight used to draw the control's text.
  165. /// </summary>
  166. public FontWeight FontWeight
  167. {
  168. get { return GetValue(FontWeightProperty); }
  169. set { SetValue(FontWeightProperty, value); }
  170. }
  171. /// <summary>
  172. /// Gets or sets the font stretch used to draw the control's text.
  173. /// </summary>
  174. public FontStretch FontStretch
  175. {
  176. get { return GetValue(FontStretchProperty); }
  177. set { SetValue(FontStretchProperty, value); }
  178. }
  179. /// <summary>
  180. /// Gets or sets the brush used to draw the control's text and other foreground elements.
  181. /// </summary>
  182. public IBrush? Foreground
  183. {
  184. get { return GetValue(ForegroundProperty); }
  185. set { SetValue(ForegroundProperty, value); }
  186. }
  187. /// <summary>
  188. /// Gets or sets the padding placed between the border of the control and its content.
  189. /// </summary>
  190. public Thickness Padding
  191. {
  192. get { return GetValue(PaddingProperty); }
  193. set { SetValue(PaddingProperty, value); }
  194. }
  195. /// <summary>
  196. /// Gets or sets the template that defines the control's appearance.
  197. /// </summary>
  198. public IControlTemplate? Template
  199. {
  200. get { return GetValue(TemplateProperty); }
  201. set { SetValue(TemplateProperty, value); }
  202. }
  203. /// <summary>
  204. /// Gets the value of the IsTemplateFocusTargetProperty attached property on a control.
  205. /// </summary>
  206. /// <param name="control">The control.</param>
  207. /// <returns>The property value.</returns>
  208. /// <see cref="SetIsTemplateFocusTarget(Control, bool)"/>
  209. public static bool GetIsTemplateFocusTarget(Control control)
  210. {
  211. return control.GetValue(IsTemplateFocusTargetProperty);
  212. }
  213. /// <summary>
  214. /// Sets the value of the IsTemplateFocusTargetProperty attached property on a control.
  215. /// </summary>
  216. /// <param name="control">The control.</param>
  217. /// <param name="value">The property value.</param>
  218. /// <remarks>
  219. /// When a control is navigated to using the keyboard, a focus adorner is shown - usually
  220. /// around the control itself. However if the TemplatedControl.IsTemplateFocusTarget
  221. /// attached property is set to true on an element in the control template, then the focus
  222. /// adorner will be shown around that control instead.
  223. /// </remarks>
  224. public static void SetIsTemplateFocusTarget(Control control, bool value)
  225. {
  226. control.SetValue(IsTemplateFocusTargetProperty, value);
  227. }
  228. /// <inheritdoc/>
  229. public sealed override void ApplyTemplate()
  230. {
  231. var template = Template;
  232. var logical = (ILogical)this;
  233. // Apply the template if it is not the same as the template already applied - except
  234. // for in the case that the template is null and we're not attached to the logical
  235. // tree. In that case, the template has probably been cleared because the style setting
  236. // the template has been detached, so we want to wait until it's re-attached to the
  237. // logical tree as if it's re-attached to the same tree the template will be the same
  238. // and we don't need to do anything.
  239. if (_appliedTemplate != template && (template != null || logical.IsAttachedToLogicalTree))
  240. {
  241. if (VisualChildren.Count > 0)
  242. {
  243. foreach (var child in this.GetTemplateChildren())
  244. {
  245. child.TemplatedParent = null;
  246. ((ISetLogicalParent)child).SetParent(null);
  247. }
  248. VisualChildren.Clear();
  249. }
  250. if (template != null)
  251. {
  252. Logger.TryGet(LogEventLevel.Verbose, LogArea.Control)?.Log(this, "Creating control template");
  253. var (child, nameScope) = template.Build(this);
  254. ApplyTemplatedParent(child, this);
  255. ((ISetLogicalParent)child).SetParent(this);
  256. VisualChildren.Add(child);
  257. var e = new TemplateAppliedEventArgs(nameScope);
  258. OnApplyTemplate(e);
  259. RaiseEvent(e);
  260. }
  261. _appliedTemplate = template;
  262. }
  263. }
  264. /// <inheritdoc/>
  265. protected override Control GetTemplateFocusTarget()
  266. {
  267. foreach (Control child in this.GetTemplateChildren())
  268. {
  269. if (GetIsTemplateFocusTarget(child))
  270. {
  271. return child;
  272. }
  273. }
  274. return this;
  275. }
  276. /// <inheritdoc />
  277. protected sealed override void NotifyChildResourcesChanged(ResourcesChangedEventArgs e)
  278. {
  279. var count = VisualChildren.Count;
  280. for (var i = 0; i < count; ++i)
  281. {
  282. if (VisualChildren[i] is ILogical logical)
  283. {
  284. logical.NotifyResourcesChanged(e);
  285. }
  286. }
  287. base.NotifyChildResourcesChanged(e);
  288. }
  289. /// <inheritdoc/>
  290. protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
  291. {
  292. if (VisualChildren.Count > 0)
  293. {
  294. ((ILogical)VisualChildren[0]).NotifyAttachedToLogicalTree(e);
  295. }
  296. base.OnAttachedToLogicalTree(e);
  297. }
  298. /// <inheritdoc/>
  299. protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
  300. {
  301. if (VisualChildren.Count > 0)
  302. {
  303. ((ILogical)VisualChildren[0]).NotifyDetachedFromLogicalTree(e);
  304. }
  305. base.OnDetachedFromLogicalTree(e);
  306. }
  307. /// <summary>
  308. /// Called when the control's template is applied.
  309. /// In simple terms, this means the method is called just before the control is displayed.
  310. /// </summary>
  311. /// <param name="e">The event args.</param>
  312. protected virtual void OnApplyTemplate(TemplateAppliedEventArgs e)
  313. {
  314. }
  315. /// <summary>
  316. /// Called when the <see cref="Template"/> property changes.
  317. /// </summary>
  318. /// <param name="e">The event args.</param>
  319. protected virtual void OnTemplateChanged(AvaloniaPropertyChangedEventArgs e)
  320. {
  321. InvalidateMeasure();
  322. }
  323. /// <summary>
  324. /// Sets the TemplatedParent property for the created template children.
  325. /// </summary>
  326. /// <param name="control">The control.</param>
  327. /// <param name="templatedParent">The templated parent to apply.</param>
  328. internal static void ApplyTemplatedParent(StyledElement control, AvaloniaObject? templatedParent)
  329. {
  330. control.TemplatedParent = templatedParent;
  331. var children = control.LogicalChildren;
  332. var count = children.Count;
  333. for (var i = 0; i < count; i++)
  334. {
  335. if (children[i] is StyledElement child && child.TemplatedParent is null)
  336. {
  337. ApplyTemplatedParent(child, templatedParent);
  338. }
  339. }
  340. }
  341. private protected override void OnControlThemeChanged()
  342. {
  343. base.OnControlThemeChanged();
  344. var count = VisualChildren.Count;
  345. for (var i = 0; i < count; ++i)
  346. {
  347. if (VisualChildren[i] is StyledElement child &&
  348. child.TemplatedParent == this)
  349. {
  350. child.OnTemplatedParentControlThemeChanged();
  351. }
  352. }
  353. }
  354. }
  355. }