Control.cs 29 KB

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