Control.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922
  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.Collections.Specialized;
  6. using System.Linq;
  7. using System.Reactive;
  8. using System.Reactive.Linq;
  9. using System.Reactive.Subjects;
  10. using Avalonia.Collections;
  11. using Avalonia.Controls.Primitives;
  12. using Avalonia.Controls.Templates;
  13. using Avalonia.Data;
  14. using Avalonia.Diagnostics;
  15. using Avalonia.Input;
  16. using Avalonia.Interactivity;
  17. using Avalonia.Logging;
  18. using Avalonia.LogicalTree;
  19. using Avalonia.Rendering;
  20. using Avalonia.Styling;
  21. using Avalonia.VisualTree;
  22. namespace Avalonia.Controls
  23. {
  24. /// <summary>
  25. /// Base class for Avalonia controls.
  26. /// </summary>
  27. /// <remarks>
  28. /// The control class extends <see cref="InputElement"/> and adds the following features:
  29. ///
  30. /// - An inherited <see cref="DataContext"/>.
  31. /// - A <see cref="Tag"/> property to allow user-defined data to be attached to the control.
  32. /// - A collection of class strings for custom styling.
  33. /// - Implements <see cref="IStyleable"/> to allow styling to work on the control.
  34. /// - Implements <see cref="ILogical"/> to form part of a logical tree.
  35. /// </remarks>
  36. public class Control : InputElement, IControl, INamed, ISetInheritanceParent, ISetLogicalParent, ISupportInitialize, IVisualBrushInitialize
  37. {
  38. /// <summary>
  39. /// Defines the <see cref="DataContext"/> property.
  40. /// </summary>
  41. public static readonly StyledProperty<object> DataContextProperty =
  42. AvaloniaProperty.Register<Control, object>(
  43. nameof(DataContext),
  44. inherits: true,
  45. notifying: DataContextNotifying);
  46. /// <summary>
  47. /// Defines the <see cref="FocusAdorner"/> property.
  48. /// </summary>
  49. public static readonly StyledProperty<ITemplate<IControl>> FocusAdornerProperty =
  50. AvaloniaProperty.Register<Control, ITemplate<IControl>>(nameof(FocusAdorner));
  51. /// <summary>
  52. /// Defines the <see cref="Name"/> property.
  53. /// </summary>
  54. public static readonly DirectProperty<Control, string> NameProperty =
  55. AvaloniaProperty.RegisterDirect<Control, string>(nameof(Name), o => o.Name, (o, v) => o.Name = v);
  56. /// <summary>
  57. /// Defines the <see cref="Parent"/> property.
  58. /// </summary>
  59. public static readonly DirectProperty<Control, IControl> ParentProperty =
  60. AvaloniaProperty.RegisterDirect<Control, IControl>(nameof(Parent), o => o.Parent);
  61. /// <summary>
  62. /// Defines the <see cref="Tag"/> property.
  63. /// </summary>
  64. public static readonly StyledProperty<object> TagProperty =
  65. AvaloniaProperty.Register<Control, object>(nameof(Tag));
  66. /// <summary>
  67. /// Defines the <see cref="TemplatedParent"/> property.
  68. /// </summary>
  69. public static readonly StyledProperty<ITemplatedControl> TemplatedParentProperty =
  70. AvaloniaProperty.Register<Control, ITemplatedControl>(nameof(TemplatedParent), inherits: true);
  71. /// <summary>
  72. /// Defines the <see cref="ContextMenu"/> property.
  73. /// </summary>
  74. public static readonly StyledProperty<ContextMenu> ContextMenuProperty =
  75. AvaloniaProperty.Register<Control, ContextMenu>(nameof(ContextMenu));
  76. /// <summary>
  77. /// Event raised when an element wishes to be scrolled into view.
  78. /// </summary>
  79. public static readonly RoutedEvent<RequestBringIntoViewEventArgs> RequestBringIntoViewEvent =
  80. RoutedEvent.Register<Control, RequestBringIntoViewEventArgs>("RequestBringIntoView", RoutingStrategies.Bubble);
  81. private int _initCount;
  82. private string _name;
  83. private IControl _parent;
  84. private readonly Classes _classes = new Classes();
  85. private DataTemplates _dataTemplates;
  86. private IControl _focusAdorner;
  87. private bool _isAttachedToLogicalTree;
  88. private IAvaloniaList<ILogical> _logicalChildren;
  89. private INameScope _nameScope;
  90. private ResourceDictionary _resources;
  91. private Styles _styles;
  92. private bool _styled;
  93. private Subject<IStyleable> _styleDetach = new Subject<IStyleable>();
  94. /// <summary>
  95. /// Initializes static members of the <see cref="Control"/> class.
  96. /// </summary>
  97. static Control()
  98. {
  99. AffectsMeasure(IsVisibleProperty);
  100. PseudoClass(IsEnabledCoreProperty, x => !x, ":disabled");
  101. PseudoClass(IsFocusedProperty, ":focus");
  102. PseudoClass(IsPointerOverProperty, ":pointerover");
  103. }
  104. /// <summary>
  105. /// Initializes a new instance of the <see cref="Control"/> class.
  106. /// </summary>
  107. public Control()
  108. {
  109. _nameScope = this as INameScope;
  110. _isAttachedToLogicalTree = this is IStyleRoot;
  111. }
  112. /// <summary>
  113. /// Raised when the control is attached to a rooted logical tree.
  114. /// </summary>
  115. public event EventHandler<LogicalTreeAttachmentEventArgs> AttachedToLogicalTree;
  116. /// <summary>
  117. /// Raised when the control is detached from a rooted logical tree.
  118. /// </summary>
  119. public event EventHandler<LogicalTreeAttachmentEventArgs> DetachedFromLogicalTree;
  120. /// <summary>
  121. /// Occurs when the <see cref="DataContext"/> property changes.
  122. /// </summary>
  123. /// <remarks>
  124. /// This event will be raised when the <see cref="DataContext"/> property has changed and
  125. /// all subscribers to that change have been notified.
  126. /// </remarks>
  127. public event EventHandler DataContextChanged;
  128. /// <summary>
  129. /// Occurs when the control has finished initialization.
  130. /// </summary>
  131. /// <remarks>
  132. /// The Initialized event indicates that all property values on the control have been set.
  133. /// When loading the control from markup, it occurs when
  134. /// <see cref="ISupportInitialize.EndInit"/> is called *and* the control
  135. /// is attached to a rooted logical tree. When the control is created by code and
  136. /// <see cref="ISupportInitialize"/> is not used, it is called when the control is attached
  137. /// to the visual tree.
  138. /// </remarks>
  139. public event EventHandler Initialized;
  140. /// <summary>
  141. /// Occurs when a resource in this control or a parent control has changed.
  142. /// </summary>
  143. public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
  144. /// <summary>
  145. /// Gets or sets the name of the control.
  146. /// </summary>
  147. /// <remarks>
  148. /// An element's name is used to uniquely identify a control within the control's name
  149. /// scope. Once the element is added to a logical tree, its name cannot be changed.
  150. /// </remarks>
  151. public string Name
  152. {
  153. get
  154. {
  155. return _name;
  156. }
  157. set
  158. {
  159. if (value.Trim() == string.Empty)
  160. {
  161. throw new InvalidOperationException("Cannot set Name to empty string.");
  162. }
  163. if (_styled)
  164. {
  165. throw new InvalidOperationException("Cannot set Name : control already styled.");
  166. }
  167. _name = value;
  168. }
  169. }
  170. /// <summary>
  171. /// Gets or sets the control's classes.
  172. /// </summary>
  173. /// <remarks>
  174. /// <para>
  175. /// Classes can be used to apply user-defined styling to controls, or to allow controls
  176. /// that share a common purpose to be easily selected.
  177. /// </para>
  178. /// <para>
  179. /// Even though this property can be set, the setter is only intended for use in object
  180. /// initializers. Assigning to this property does not change the underlying collection,
  181. /// it simply clears the existing collection and addds the contents of the assigned
  182. /// collection.
  183. /// </para>
  184. /// </remarks>
  185. public Classes Classes
  186. {
  187. get
  188. {
  189. return _classes;
  190. }
  191. set
  192. {
  193. if (_classes != value)
  194. {
  195. _classes.Replace(value);
  196. }
  197. }
  198. }
  199. /// <summary>
  200. /// Gets or sets the control's data context.
  201. /// </summary>
  202. /// <remarks>
  203. /// The data context is an inherited property that specifies the default object that will
  204. /// be used for data binding.
  205. /// </remarks>
  206. public object DataContext
  207. {
  208. get { return GetValue(DataContextProperty); }
  209. set { SetValue(DataContextProperty, value); }
  210. }
  211. /// <summary>
  212. /// Gets or sets the control's focus adorner.
  213. /// </summary>
  214. public ITemplate<IControl> FocusAdorner
  215. {
  216. get { return GetValue(FocusAdornerProperty); }
  217. set { SetValue(FocusAdornerProperty, value); }
  218. }
  219. /// <summary>
  220. /// Gets or sets the data templates for the control.
  221. /// </summary>
  222. /// <remarks>
  223. /// Each control may define data templates which are applied to the control itself and its
  224. /// children.
  225. /// </remarks>
  226. public DataTemplates DataTemplates
  227. {
  228. get { return _dataTemplates ?? (_dataTemplates = new DataTemplates()); }
  229. set { _dataTemplates = value; }
  230. }
  231. /// <summary>
  232. /// Gets a value that indicates whether the element has finished initialization.
  233. /// </summary>
  234. /// <remarks>
  235. /// For more information about when IsInitialized is set, see the <see cref="Initialized"/>
  236. /// event.
  237. /// </remarks>
  238. public bool IsInitialized { get; private set; }
  239. /// <summary>
  240. /// Gets or sets the styles for the control.
  241. /// </summary>
  242. /// <remarks>
  243. /// Styles for the entire application are added to the Application.Styles collection, but
  244. /// each control may in addition define its own styles which are applied to the control
  245. /// itself and its children.
  246. /// </remarks>
  247. public Styles Styles
  248. {
  249. get { return _styles ?? (Styles = new Styles()); }
  250. set
  251. {
  252. Contract.Requires<ArgumentNullException>(value != null);
  253. if (_styles != value)
  254. {
  255. if (_styles != null)
  256. {
  257. (_styles as ISetStyleParent)?.SetParent(null);
  258. _styles.ResourcesChanged -= ThisResourcesChanged;
  259. }
  260. _styles = value;
  261. if (value is ISetStyleParent setParent && setParent.ResourceParent == null)
  262. {
  263. setParent.SetParent(this);
  264. }
  265. _styles.ResourcesChanged += ThisResourcesChanged;
  266. }
  267. }
  268. }
  269. /// <summary>
  270. /// Gets the control's logical parent.
  271. /// </summary>
  272. public IControl Parent => _parent;
  273. /// <summary>
  274. /// Gets or sets a context menu to the control.
  275. /// </summary>
  276. public ContextMenu ContextMenu
  277. {
  278. get { return GetValue(ContextMenuProperty); }
  279. set { SetValue(ContextMenuProperty, value); }
  280. }
  281. /// <summary>
  282. /// Gets or sets the control's resource dictionary.
  283. /// </summary>
  284. public IResourceDictionary Resources
  285. {
  286. get
  287. {
  288. if (_resources == null)
  289. {
  290. _resources = new ResourceDictionary();
  291. _resources.ResourcesChanged += ThisResourcesChanged;
  292. }
  293. return _resources;
  294. }
  295. }
  296. /// <summary>
  297. /// Gets or sets a user-defined object attached to the control.
  298. /// </summary>
  299. public object Tag
  300. {
  301. get { return GetValue(TagProperty); }
  302. set { SetValue(TagProperty, value); }
  303. }
  304. /// <summary>
  305. /// Gets the control whose lookless template this control is part of.
  306. /// </summary>
  307. public ITemplatedControl TemplatedParent
  308. {
  309. get { return GetValue(TemplatedParentProperty); }
  310. internal set { SetValue(TemplatedParentProperty, value); }
  311. }
  312. /// <summary>
  313. /// Gets the control's logical children.
  314. /// </summary>
  315. protected IAvaloniaList<ILogical> LogicalChildren
  316. {
  317. get
  318. {
  319. if (_logicalChildren == null)
  320. {
  321. var list = new AvaloniaList<ILogical>();
  322. list.ResetBehavior = ResetBehavior.Remove;
  323. list.Validate = ValidateLogicalChild;
  324. list.CollectionChanged += LogicalChildrenCollectionChanged;
  325. _logicalChildren = list;
  326. }
  327. return _logicalChildren;
  328. }
  329. }
  330. /// <summary>
  331. /// Gets the <see cref="Classes"/> collection in a form that allows adding and removing
  332. /// pseudoclasses.
  333. /// </summary>
  334. protected IPseudoClasses PseudoClasses => Classes;
  335. /// <summary>
  336. /// Gets a value indicating whether the element is attached to a rooted logical tree.
  337. /// </summary>
  338. bool ILogical.IsAttachedToLogicalTree => _isAttachedToLogicalTree;
  339. /// <summary>
  340. /// Gets the control's logical parent.
  341. /// </summary>
  342. ILogical ILogical.LogicalParent => Parent;
  343. /// <summary>
  344. /// Gets the control's logical children.
  345. /// </summary>
  346. IAvaloniaReadOnlyList<ILogical> ILogical.LogicalChildren => LogicalChildren;
  347. /// <inheritdoc/>
  348. bool IResourceProvider.HasResources => _resources?.Count > 0 || Styles.HasResources;
  349. /// <inheritdoc/>
  350. IResourceProvider IResourceProvider.ResourceParent => ((IStyleHost)this).StylingParent as IResourceProvider;
  351. /// <inheritdoc/>
  352. IAvaloniaReadOnlyList<string> IStyleable.Classes => Classes;
  353. /// <summary>
  354. /// Gets the type by which the control is styled.
  355. /// </summary>
  356. /// <remarks>
  357. /// Usually controls are styled by their own type, but there are instances where you want
  358. /// a control to be styled by its base type, e.g. creating SpecialButton that
  359. /// derives from Button and adds extra functionality but is still styled as a regular
  360. /// Button.
  361. /// </remarks>
  362. Type IStyleable.StyleKey => GetType();
  363. /// <inheritdoc/>
  364. IObservable<IStyleable> IStyleable.StyleDetach => _styleDetach;
  365. /// <inheritdoc/>
  366. IStyleHost IStyleHost.StylingParent => (IStyleHost)InheritanceParent;
  367. /// <inheritdoc/>
  368. public virtual void BeginInit()
  369. {
  370. ++_initCount;
  371. }
  372. /// <inheritdoc/>
  373. public virtual void EndInit()
  374. {
  375. if (_initCount == 0)
  376. {
  377. throw new InvalidOperationException("BeginInit was not called.");
  378. }
  379. if (--_initCount == 0 && _isAttachedToLogicalTree)
  380. {
  381. InitializeStylesIfNeeded();
  382. InitializeIfNeeded();
  383. }
  384. }
  385. private void InitializeStylesIfNeeded(bool force = false)
  386. {
  387. if (_initCount == 0 && (!_styled || force))
  388. {
  389. RegisterWithNameScope();
  390. ApplyStyling();
  391. _styled = true;
  392. }
  393. }
  394. private void InitializeIfNeeded()
  395. {
  396. if (_initCount == 0 && !IsInitialized)
  397. {
  398. IsInitialized = true;
  399. Initialized?.Invoke(this, EventArgs.Empty);
  400. }
  401. }
  402. /// <inheritdoc/>
  403. void ILogical.NotifyAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
  404. {
  405. this.OnAttachedToLogicalTreeCore(e);
  406. }
  407. /// <inheritdoc/>
  408. void ILogical.NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
  409. {
  410. this.OnDetachedFromLogicalTreeCore(e);
  411. }
  412. /// <inheritdoc/>
  413. void ILogical.NotifyResourcesChanged(ResourcesChangedEventArgs e)
  414. {
  415. ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
  416. foreach (var child in LogicalChildren)
  417. {
  418. child.NotifyResourcesChanged(e);
  419. }
  420. }
  421. /// <inheritdoc/>
  422. bool IResourceProvider.TryGetResource(string key, out object value)
  423. {
  424. value = null;
  425. return (_resources?.TryGetResource(key, out value) ?? false) ||
  426. (_styles?.TryGetResource(key, out value) ?? false);
  427. }
  428. /// <summary>
  429. /// Sets the control's logical parent.
  430. /// </summary>
  431. /// <param name="parent">The parent.</param>
  432. void ISetLogicalParent.SetParent(ILogical parent)
  433. {
  434. var old = Parent;
  435. if (parent != old)
  436. {
  437. if (old != null && parent != null)
  438. {
  439. throw new InvalidOperationException("The Control already has a parent.");
  440. }
  441. if (_isAttachedToLogicalTree)
  442. {
  443. var oldRoot = FindStyleRoot(old) ?? this as IStyleRoot;
  444. if (oldRoot == null)
  445. {
  446. throw new AvaloniaInternalException("Was attached to logical tree but cannot find root.");
  447. }
  448. var e = new LogicalTreeAttachmentEventArgs(oldRoot);
  449. OnDetachedFromLogicalTreeCore(e);
  450. }
  451. if (InheritanceParent == null || parent == null)
  452. {
  453. InheritanceParent = parent as AvaloniaObject;
  454. }
  455. _parent = (IControl)parent;
  456. ((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
  457. if (_parent is IStyleRoot || _parent?.IsAttachedToLogicalTree == true || this is IStyleRoot)
  458. {
  459. var newRoot = FindStyleRoot(this);
  460. if (newRoot == null)
  461. {
  462. throw new AvaloniaInternalException("Parent is atttached to logical tree but cannot find root.");
  463. }
  464. var e = new LogicalTreeAttachmentEventArgs(newRoot);
  465. OnAttachedToLogicalTreeCore(e);
  466. }
  467. RaisePropertyChanged(ParentProperty, old, _parent, BindingPriority.LocalValue);
  468. }
  469. }
  470. /// <summary>
  471. /// Sets the control's inheritance parent.
  472. /// </summary>
  473. /// <param name="parent">The parent.</param>
  474. void ISetInheritanceParent.SetParent(IAvaloniaObject parent)
  475. {
  476. InheritanceParent = parent;
  477. }
  478. /// <inheritdoc/>
  479. void IVisualBrushInitialize.EnsureInitialized()
  480. {
  481. if (VisualRoot == null)
  482. {
  483. if (!IsInitialized)
  484. {
  485. foreach (var i in this.GetSelfAndVisualDescendants())
  486. {
  487. var c = i as IControl;
  488. if (c?.IsInitialized == false)
  489. {
  490. var init = c as ISupportInitialize;
  491. if (init != null)
  492. {
  493. init.BeginInit();
  494. init.EndInit();
  495. }
  496. }
  497. }
  498. }
  499. if (!IsArrangeValid)
  500. {
  501. Measure(Size.Infinity);
  502. Arrange(new Rect(DesiredSize));
  503. }
  504. }
  505. }
  506. /// <summary>
  507. /// Adds a pseudo-class to be set when a property is true.
  508. /// </summary>
  509. /// <param name="property">The property.</param>
  510. /// <param name="className">The pseudo-class.</param>
  511. protected static void PseudoClass(AvaloniaProperty<bool> property, string className)
  512. {
  513. PseudoClass(property, x => x, className);
  514. }
  515. /// <summary>
  516. /// Adds a pseudo-class to be set when a property equals a certain value.
  517. /// </summary>
  518. /// <typeparam name="T">The type of the property.</typeparam>
  519. /// <param name="property">The property.</param>
  520. /// <param name="selector">Returns a boolean value based on the property value.</param>
  521. /// <param name="className">The pseudo-class.</param>
  522. protected static void PseudoClass<T>(
  523. AvaloniaProperty<T> property,
  524. Func<T, bool> selector,
  525. string className)
  526. {
  527. Contract.Requires<ArgumentNullException>(property != null);
  528. Contract.Requires<ArgumentNullException>(selector != null);
  529. Contract.Requires<ArgumentNullException>(className != null);
  530. Contract.Requires<ArgumentNullException>(property != null);
  531. if (string.IsNullOrWhiteSpace(className))
  532. {
  533. throw new ArgumentException("Cannot supply an empty className.");
  534. }
  535. property.Changed.Merge(property.Initialized)
  536. .Where(e => e.Sender is Control)
  537. .Subscribe(e =>
  538. {
  539. if (selector((T)e.NewValue))
  540. {
  541. ((Control)e.Sender).PseudoClasses.Add(className);
  542. }
  543. else
  544. {
  545. ((Control)e.Sender).PseudoClasses.Remove(className);
  546. }
  547. });
  548. }
  549. /// <summary>
  550. /// Gets the element that recieves the focus adorner.
  551. /// </summary>
  552. /// <returns>The control that recieves the focus adorner.</returns>
  553. protected virtual IControl GetTemplateFocusTarget()
  554. {
  555. return this;
  556. }
  557. /// <summary>
  558. /// Called when the control is added to a rooted logical tree.
  559. /// </summary>
  560. /// <param name="e">The event args.</param>
  561. protected virtual void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
  562. {
  563. AttachedToLogicalTree?.Invoke(this, e);
  564. }
  565. /// <summary>
  566. /// Called when the control is removed from a rooted logical tree.
  567. /// </summary>
  568. /// <param name="e">The event args.</param>
  569. protected virtual void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
  570. {
  571. DetachedFromLogicalTree?.Invoke(this, e);
  572. }
  573. /// <inheritdoc/>
  574. protected sealed override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
  575. {
  576. base.OnAttachedToVisualTreeCore(e);
  577. InitializeIfNeeded();
  578. }
  579. /// <inheritdoc/>
  580. protected sealed override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
  581. {
  582. base.OnDetachedFromVisualTreeCore(e);
  583. }
  584. /// <summary>
  585. /// Called before the <see cref="DataContext"/> property changes.
  586. /// </summary>
  587. protected virtual void OnDataContextChanging()
  588. {
  589. }
  590. /// <summary>
  591. /// Called after the <see cref="DataContext"/> property changes.
  592. /// </summary>
  593. protected virtual void OnDataContextChanged()
  594. {
  595. DataContextChanged?.Invoke(this, EventArgs.Empty);
  596. }
  597. /// <inheritdoc/>
  598. protected override void OnGotFocus(GotFocusEventArgs e)
  599. {
  600. base.OnGotFocus(e);
  601. if (IsFocused &&
  602. (e.NavigationMethod == NavigationMethod.Tab ||
  603. e.NavigationMethod == NavigationMethod.Directional))
  604. {
  605. var adornerLayer = AdornerLayer.GetAdornerLayer(this);
  606. if (adornerLayer != null)
  607. {
  608. if (_focusAdorner == null)
  609. {
  610. var template = GetValue(FocusAdornerProperty);
  611. if (template != null)
  612. {
  613. _focusAdorner = template.Build();
  614. }
  615. }
  616. if (_focusAdorner != null)
  617. {
  618. var target = (Visual)GetTemplateFocusTarget();
  619. if (target != null)
  620. {
  621. AdornerLayer.SetAdornedElement((Visual)_focusAdorner, target);
  622. adornerLayer.Children.Add(_focusAdorner);
  623. }
  624. }
  625. }
  626. }
  627. }
  628. /// <inheritdoc/>
  629. protected override void OnLostFocus(RoutedEventArgs e)
  630. {
  631. base.OnLostFocus(e);
  632. if (_focusAdorner != null)
  633. {
  634. var adornerLayer = (IPanel)_focusAdorner.Parent;
  635. adornerLayer.Children.Remove(_focusAdorner);
  636. _focusAdorner = null;
  637. }
  638. }
  639. /// <summary>
  640. /// Called when the <see cref="DataContext"/> property begins and ends being notified.
  641. /// </summary>
  642. /// <param name="o">The object on which the DataContext is changing.</param>
  643. /// <param name="notifying">Whether the notifcation is beginning or ending.</param>
  644. private static void DataContextNotifying(IAvaloniaObject o, bool notifying)
  645. {
  646. var control = o as Control;
  647. if (control != null)
  648. {
  649. if (notifying)
  650. {
  651. control.OnDataContextChanging();
  652. }
  653. else
  654. {
  655. control.OnDataContextChanged();
  656. }
  657. }
  658. }
  659. private static IStyleRoot FindStyleRoot(IStyleHost e)
  660. {
  661. while (e != null)
  662. {
  663. var root = e as IStyleRoot;
  664. if (root != null && root.StylingParent == null)
  665. {
  666. return root;
  667. }
  668. e = e.StylingParent;
  669. }
  670. return null;
  671. }
  672. private void ApplyStyling()
  673. {
  674. AvaloniaLocator.Current.GetService<IStyler>()?.ApplyStyles(this);
  675. }
  676. private void RegisterWithNameScope()
  677. {
  678. if (_nameScope == null)
  679. {
  680. _nameScope = NameScope.GetNameScope(this) ?? ((Control)Parent)?._nameScope;
  681. }
  682. if (Name != null)
  683. {
  684. _nameScope?.Register(Name, this);
  685. var visualParent = Parent as Visual;
  686. if (this is INameScope && visualParent != null)
  687. {
  688. // If we have e.g. a named UserControl in a window then we want that control
  689. // to be findable by name from the Window, so register with both name scopes.
  690. // This differs from WPF's behavior in that XAML manually registers controls
  691. // with name scopes based on the XAML file in which the name attribute appears,
  692. // but we're trying to avoid XAML magic in Avalonia in order to made code-
  693. // created UIs easy. This will cause problems if a UserControl declares a name
  694. // in its XAML and that control is included multiple times in a parent control
  695. // (as the name will be duplicated), however at the moment I'm fine with saying
  696. // "don't do that".
  697. var parentNameScope = NameScope.FindNameScope(visualParent);
  698. parentNameScope?.Register(Name, this);
  699. }
  700. }
  701. }
  702. private static void ValidateLogicalChild(ILogical c)
  703. {
  704. if (c == null)
  705. {
  706. throw new ArgumentException("Cannot add null to LogicalChildren.");
  707. }
  708. }
  709. private void OnAttachedToLogicalTreeCore(LogicalTreeAttachmentEventArgs e)
  710. {
  711. // This method can be called when a control is already attached to the logical tree
  712. // in the following scenario:
  713. // - ListBox gets assigned Items containing ListBoxItem
  714. // - ListBox makes ListBoxItem a logical child
  715. // - ListBox template gets applied; making its Panel get attached to logical tree
  716. // - That AttachedToLogicalTree signal travels down to the ListBoxItem
  717. if (!_isAttachedToLogicalTree)
  718. {
  719. _isAttachedToLogicalTree = true;
  720. InitializeStylesIfNeeded(true);
  721. OnAttachedToLogicalTree(e);
  722. }
  723. foreach (var child in LogicalChildren.OfType<Control>())
  724. {
  725. child.OnAttachedToLogicalTreeCore(e);
  726. }
  727. }
  728. private void OnDetachedFromLogicalTreeCore(LogicalTreeAttachmentEventArgs e)
  729. {
  730. if (_isAttachedToLogicalTree)
  731. {
  732. if (Name != null)
  733. {
  734. _nameScope?.Unregister(Name);
  735. }
  736. _isAttachedToLogicalTree = false;
  737. _styleDetach.OnNext(this);
  738. OnDetachedFromLogicalTree(e);
  739. foreach (var child in LogicalChildren.OfType<Control>())
  740. {
  741. child.OnDetachedFromLogicalTreeCore(e);
  742. }
  743. #if DEBUG
  744. if (((INotifyCollectionChangedDebug)_classes).GetCollectionChangedSubscribers()?.Length > 0)
  745. {
  746. Logger.Warning(
  747. LogArea.Control,
  748. this,
  749. "{Type} detached from logical tree but still has class listeners",
  750. this.GetType());
  751. }
  752. #endif
  753. }
  754. }
  755. private void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  756. {
  757. switch (e.Action)
  758. {
  759. case NotifyCollectionChangedAction.Add:
  760. SetLogicalParent(e.NewItems.Cast<ILogical>());
  761. break;
  762. case NotifyCollectionChangedAction.Remove:
  763. ClearLogicalParent(e.OldItems.Cast<ILogical>());
  764. break;
  765. case NotifyCollectionChangedAction.Replace:
  766. ClearLogicalParent(e.OldItems.Cast<ILogical>());
  767. SetLogicalParent(e.NewItems.Cast<ILogical>());
  768. break;
  769. case NotifyCollectionChangedAction.Reset:
  770. throw new NotSupportedException("Reset should not be signalled on LogicalChildren collection");
  771. }
  772. }
  773. private void SetLogicalParent(IEnumerable<ILogical> children)
  774. {
  775. foreach (var i in children)
  776. {
  777. if (i.LogicalParent == null)
  778. {
  779. ((ISetLogicalParent)i).SetParent(this);
  780. }
  781. }
  782. }
  783. private void ClearLogicalParent(IEnumerable<ILogical> children)
  784. {
  785. foreach (var i in children)
  786. {
  787. if (i.LogicalParent == this)
  788. {
  789. ((ISetLogicalParent)i).SetParent(null);
  790. }
  791. }
  792. }
  793. private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e)
  794. {
  795. ((ILogical)this).NotifyResourcesChanged(e);
  796. }
  797. }
  798. }