Control.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using Avalonia.Automation.Peers;
  5. using Avalonia.Controls.Primitives;
  6. using Avalonia.Controls.Templates;
  7. using Avalonia.Input;
  8. using Avalonia.Input.Platform;
  9. using Avalonia.Interactivity;
  10. using Avalonia.LogicalTree;
  11. using Avalonia.Rendering;
  12. using Avalonia.Styling;
  13. using Avalonia.Threading;
  14. using Avalonia.VisualTree;
  15. namespace Avalonia.Controls
  16. {
  17. /// <summary>
  18. /// Base class for Avalonia controls.
  19. /// </summary>
  20. /// <remarks>
  21. /// The control class extends <see cref="InputElement"/> and adds the following features:
  22. ///
  23. /// - A <see cref="Tag"/> property to allow user-defined data to be attached to the control.
  24. /// - <see cref="ContextRequestedEvent"/> and other context menu related members.
  25. /// </remarks>
  26. public class Control : InputElement, IDataTemplateHost, IVisualBrushInitialize, ISetterValue
  27. {
  28. /// <summary>
  29. /// Defines the <see cref="FocusAdorner"/> property.
  30. /// </summary>
  31. public static readonly StyledProperty<ITemplate<Control>?> FocusAdornerProperty =
  32. AvaloniaProperty.Register<Control, ITemplate<Control>?>(nameof(FocusAdorner));
  33. /// <summary>
  34. /// Defines the <see cref="Tag"/> property.
  35. /// </summary>
  36. public static readonly StyledProperty<object?> TagProperty =
  37. AvaloniaProperty.Register<Control, object?>(nameof(Tag));
  38. /// <summary>
  39. /// Defines the <see cref="ContextMenu"/> property.
  40. /// </summary>
  41. public static readonly StyledProperty<ContextMenu?> ContextMenuProperty =
  42. AvaloniaProperty.Register<Control, ContextMenu?>(nameof(ContextMenu));
  43. /// <summary>
  44. /// Defines the <see cref="ContextFlyout"/> property
  45. /// </summary>
  46. public static readonly StyledProperty<FlyoutBase?> ContextFlyoutProperty =
  47. AvaloniaProperty.Register<Control, FlyoutBase?>(nameof(ContextFlyout));
  48. /// <summary>
  49. /// Event raised when an element wishes to be scrolled into view.
  50. /// </summary>
  51. public static readonly RoutedEvent<RequestBringIntoViewEventArgs> RequestBringIntoViewEvent =
  52. RoutedEvent.Register<Control, RequestBringIntoViewEventArgs>(
  53. "RequestBringIntoView",
  54. RoutingStrategies.Bubble);
  55. /// <summary>
  56. /// Provides event data for the <see cref="ContextRequested"/> event.
  57. /// </summary>
  58. public static readonly RoutedEvent<ContextRequestedEventArgs> ContextRequestedEvent =
  59. RoutedEvent.Register<Control, ContextRequestedEventArgs>(
  60. nameof(ContextRequested),
  61. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  62. /// <summary>
  63. /// Defines the <see cref="Loaded"/> event.
  64. /// </summary>
  65. public static readonly RoutedEvent<RoutedEventArgs> LoadedEvent =
  66. RoutedEvent.Register<Control, RoutedEventArgs>(
  67. nameof(Loaded),
  68. RoutingStrategies.Direct);
  69. /// <summary>
  70. /// Defines the <see cref="Unloaded"/> event.
  71. /// </summary>
  72. public static readonly RoutedEvent<RoutedEventArgs> UnloadedEvent =
  73. RoutedEvent.Register<Control, RoutedEventArgs>(
  74. nameof(Unloaded),
  75. RoutingStrategies.Direct);
  76. /// <summary>
  77. /// Defines the <see cref="SizeChanged"/> event.
  78. /// </summary>
  79. public static readonly RoutedEvent<SizeChangedEventArgs> SizeChangedEvent =
  80. RoutedEvent.Register<Control, SizeChangedEventArgs>(
  81. nameof(SizeChanged), RoutingStrategies.Direct);
  82. // Note the following:
  83. // _loadedQueue :
  84. // Is the queue where any control will be added to indicate that its loaded
  85. // event should be scheduled and called later.
  86. // _loadedProcessingQueue :
  87. // Contains a copied snapshot of the _loadedQueue at the time when processing
  88. // starts and individual events are being fired. This was needed to avoid
  89. // exceptions if new controls were added in the Loaded event itself.
  90. private static bool _isLoadedProcessing = false;
  91. private static readonly HashSet<Control> _loadedQueue = new HashSet<Control>();
  92. private static readonly HashSet<Control> _loadedProcessingQueue = new HashSet<Control>();
  93. private LoadState _loadState = LoadState.Unloaded;
  94. private DataTemplates? _dataTemplates;
  95. private Control? _focusAdorner;
  96. private AutomationPeer? _automationPeer;
  97. /// <summary>
  98. /// Gets or sets the control's focus adorner.
  99. /// </summary>
  100. public ITemplate<Control>? FocusAdorner
  101. {
  102. get => GetValue(FocusAdornerProperty);
  103. set => SetValue(FocusAdornerProperty, value);
  104. }
  105. /// <summary>
  106. /// Gets or sets the data templates for the control.
  107. /// </summary>
  108. /// <remarks>
  109. /// Each control may define data templates which are applied to the control itself and its
  110. /// children.
  111. /// </remarks>
  112. public DataTemplates DataTemplates => _dataTemplates ??= new DataTemplates();
  113. /// <summary>
  114. /// Gets or sets a context menu to the control.
  115. /// </summary>
  116. public ContextMenu? ContextMenu
  117. {
  118. get => GetValue(ContextMenuProperty);
  119. set => SetValue(ContextMenuProperty, value);
  120. }
  121. /// <summary>
  122. /// Gets or sets a context flyout to the control
  123. /// </summary>
  124. public FlyoutBase? ContextFlyout
  125. {
  126. get => GetValue(ContextFlyoutProperty);
  127. set => SetValue(ContextFlyoutProperty, value);
  128. }
  129. /// <summary>
  130. /// Gets a value indicating whether the control is fully constructed in the visual tree
  131. /// and both layout and render are complete.
  132. /// </summary>
  133. /// <remarks>
  134. /// This is set to true while raising the <see cref="Loaded"/> event.
  135. /// </remarks>
  136. public bool IsLoaded => _loadState == LoadState.Loaded;
  137. /// <summary>
  138. /// Gets or sets a user-defined object attached to the control.
  139. /// </summary>
  140. public object? Tag
  141. {
  142. get => GetValue(TagProperty);
  143. set => SetValue(TagProperty, value);
  144. }
  145. /// <summary>
  146. /// Occurs when the user has completed a context input gesture, such as a right-click.
  147. /// </summary>
  148. public event EventHandler<ContextRequestedEventArgs>? ContextRequested
  149. {
  150. add => AddHandler(ContextRequestedEvent, value);
  151. remove => RemoveHandler(ContextRequestedEvent, value);
  152. }
  153. /// <summary>
  154. /// Occurs when the control has been fully constructed in the visual tree and both
  155. /// layout and render are complete.
  156. /// </summary>
  157. /// <remarks>
  158. /// This event is guaranteed to occur after the control template is applied and references
  159. /// to objects created after the template is applied are available. This makes it different
  160. /// from OnAttachedToVisualTree which doesn't have these references. This event occurs at the
  161. /// latest possible time in the control creation life-cycle.
  162. /// </remarks>
  163. public event EventHandler<RoutedEventArgs>? Loaded
  164. {
  165. add => AddHandler(LoadedEvent, value);
  166. remove => RemoveHandler(LoadedEvent, value);
  167. }
  168. /// <summary>
  169. /// Occurs when the control is removed from the visual tree.
  170. /// </summary>
  171. /// <remarks>
  172. /// This is API symmetrical with <see cref="Loaded"/> and exists for compatibility with other
  173. /// XAML frameworks; however, it behaves the same as OnDetachedFromVisualTree.
  174. /// </remarks>
  175. public event EventHandler<RoutedEventArgs>? Unloaded
  176. {
  177. add => AddHandler(UnloadedEvent, value);
  178. remove => RemoveHandler(UnloadedEvent, value);
  179. }
  180. /// <summary>
  181. /// Occurs when the bounds (actual size) of the control have changed.
  182. /// </summary>
  183. public event EventHandler<SizeChangedEventArgs>? SizeChanged
  184. {
  185. add => AddHandler(SizeChangedEvent, value);
  186. remove => RemoveHandler(SizeChangedEvent, value);
  187. }
  188. /// <inheritdoc/>
  189. bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null;
  190. /// <inheritdoc/>
  191. void ISetterValue.Initialize(SetterBase setter)
  192. {
  193. if (setter is Setter s && s.Property == ContextFlyoutProperty)
  194. {
  195. return; // Allow ContextFlyout to not need wrapping in <Template>
  196. }
  197. throw new InvalidOperationException(
  198. "Cannot use a control as a Setter value. Wrap the control in a <Template>.");
  199. }
  200. /// <inheritdoc/>
  201. void IVisualBrushInitialize.EnsureInitialized()
  202. {
  203. if (VisualRoot == null)
  204. {
  205. if (!IsInitialized)
  206. {
  207. foreach (var i in this.GetSelfAndVisualDescendants())
  208. {
  209. var c = i as Control;
  210. if (c?.IsInitialized == false && c is ISupportInitialize init)
  211. {
  212. init.BeginInit();
  213. init.EndInit();
  214. }
  215. }
  216. }
  217. if (!IsArrangeValid)
  218. {
  219. Measure(Size.Infinity);
  220. Arrange(new Rect(DesiredSize));
  221. }
  222. }
  223. }
  224. /// <summary>
  225. /// Gets the element that receives the focus adorner.
  226. /// </summary>
  227. /// <returns>The control that receives the focus adorner.</returns>
  228. protected virtual Control? GetTemplateFocusTarget() => this;
  229. private static Action loadedProcessingAction = () =>
  230. {
  231. // Copy the loaded queue for processing
  232. // There was a possibility of the "Collection was modified; enumeration operation may not execute."
  233. // exception when only a single hash set was used. This could happen when new controls are added
  234. // within the Loaded callback/event itself. To fix this, two hash sets are used and while one is
  235. // being processed the other accepts adding new controls to process next.
  236. _loadedProcessingQueue.Clear();
  237. foreach (Control control in _loadedQueue)
  238. {
  239. _loadedProcessingQueue.Add(control);
  240. }
  241. _loadedQueue.Clear();
  242. foreach (Control control in _loadedProcessingQueue)
  243. {
  244. control.OnLoadedCore();
  245. }
  246. _loadedProcessingQueue.Clear();
  247. _isLoadedProcessing = false;
  248. // Restart if any controls were added to the queue while processing
  249. if (_loadedQueue.Count > 0)
  250. {
  251. _isLoadedProcessing = true;
  252. Dispatcher.UIThread.Post(loadedProcessingAction!, DispatcherPriority.Loaded);
  253. }
  254. };
  255. /// <summary>
  256. /// Schedules <see cref="OnLoadedCore"/> to be called for this control.
  257. /// For performance, it will be queued with other controls.
  258. /// </summary>
  259. internal void ScheduleOnLoadedCore()
  260. {
  261. if (_loadState == LoadState.Unloaded)
  262. {
  263. bool isAdded = _loadedQueue.Add(this);
  264. _loadState = LoadState.LoadPending;
  265. if (isAdded &&
  266. _isLoadedProcessing == false)
  267. {
  268. _isLoadedProcessing = true;
  269. Dispatcher.UIThread.Post(loadedProcessingAction!, DispatcherPriority.Loaded);
  270. }
  271. }
  272. }
  273. /// <summary>
  274. /// Invoked as the first step of marking the control as loaded and raising the
  275. /// <see cref="Loaded"/> event.
  276. /// </summary>
  277. internal void OnLoadedCore()
  278. {
  279. if (_loadState == LoadState.LoadPending &&
  280. ((ILogical)this).IsAttachedToLogicalTree)
  281. {
  282. _loadState = LoadState.Loaded;
  283. OnLoaded(new RoutedEventArgs(LoadedEvent, this));
  284. }
  285. else
  286. {
  287. // We somehow got here while being detached?
  288. _loadState = LoadState.Unloaded;
  289. }
  290. }
  291. /// <summary>
  292. /// Invoked as the first step of marking the control as unloaded and raising the
  293. /// <see cref="Unloaded"/> event.
  294. /// </summary>
  295. internal void OnUnloadedCore()
  296. {
  297. switch (_loadState)
  298. {
  299. case LoadState.Loaded:
  300. _loadState = LoadState.Unloaded;
  301. OnUnloaded(new RoutedEventArgs(UnloadedEvent, this));
  302. break;
  303. case LoadState.LoadPending:
  304. // Remove from the loaded event queue here as a failsafe in case the control
  305. // is detached before the dispatcher runs the Loaded jobs.
  306. _loadedQueue.Remove(this);
  307. _loadState = LoadState.Unloaded;
  308. break;
  309. }
  310. }
  311. /// <summary>
  312. /// Invoked just before the <see cref="Loaded"/> event.
  313. /// </summary>
  314. /// <param name="e">The event args.</param>
  315. protected virtual void OnLoaded(RoutedEventArgs e)
  316. {
  317. RaiseEvent(e);
  318. }
  319. /// <summary>
  320. /// Invoked just before the <see cref="Unloaded"/> event.
  321. /// </summary>
  322. /// <param name="e">The event args.</param>
  323. protected virtual void OnUnloaded(RoutedEventArgs e)
  324. {
  325. RaiseEvent(e);
  326. }
  327. /// <summary>
  328. /// Invoked just before the <see cref="SizeChanged"/> event.
  329. /// </summary>
  330. /// <param name="e">The event args.</param>
  331. protected virtual void OnSizeChanged(SizeChangedEventArgs e)
  332. {
  333. RaiseEvent(e);
  334. }
  335. /// <inheritdoc/>
  336. protected sealed override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
  337. {
  338. base.OnAttachedToVisualTreeCore(e);
  339. InitializeIfNeeded();
  340. ScheduleOnLoadedCore();
  341. Holding += OnHoldEvent;
  342. }
  343. private void OnHoldEvent(object? sender, HoldingRoutedEventArgs e)
  344. {
  345. if (e.Source == this && !e.Handled && e.HoldingState == HoldingState.Started)
  346. {
  347. // Trigger ContentRequest when hold has started
  348. var contextEvent = e.PointerEventArgs is { } ev ? new ContextRequestedEventArgs(ev) : new ContextRequestedEventArgs();
  349. RaiseEvent(contextEvent);
  350. e.Handled = contextEvent.Handled;
  351. }
  352. }
  353. /// <inheritdoc/>
  354. protected sealed override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
  355. {
  356. base.OnDetachedFromVisualTreeCore(e);
  357. OnUnloadedCore();
  358. Holding -= OnHoldEvent;
  359. }
  360. /// <inheritdoc/>
  361. protected override void OnGotFocus(GotFocusEventArgs e)
  362. {
  363. base.OnGotFocus(e);
  364. if (IsFocused &&
  365. (e.NavigationMethod == NavigationMethod.Tab ||
  366. e.NavigationMethod == NavigationMethod.Directional))
  367. {
  368. var adornerLayer = AdornerLayer.GetAdornerLayer(this);
  369. if (adornerLayer != null)
  370. {
  371. if (_focusAdorner == null)
  372. {
  373. var template = IsSet(FocusAdornerProperty)
  374. ? GetValue(FocusAdornerProperty)
  375. : adornerLayer.DefaultFocusAdorner;
  376. if (template != null)
  377. {
  378. _focusAdorner = template.Build();
  379. }
  380. }
  381. if (_focusAdorner != null && GetTemplateFocusTarget() is Visual target)
  382. {
  383. AdornerLayer.SetAdornedElement((Visual)_focusAdorner, target);
  384. adornerLayer.Children.Add(_focusAdorner);
  385. }
  386. }
  387. }
  388. }
  389. /// <inheritdoc/>
  390. protected override void OnLostFocus(RoutedEventArgs e)
  391. {
  392. base.OnLostFocus(e);
  393. if (_focusAdorner?.Parent != null)
  394. {
  395. var adornerLayer = (Panel)_focusAdorner.Parent;
  396. adornerLayer.Children.Remove(_focusAdorner);
  397. _focusAdorner = null;
  398. }
  399. }
  400. /// <summary>
  401. /// Returns a new, type-specific <see cref="AutomationPeer"/> implementation for the control.
  402. /// </summary>
  403. /// <returns>The type-specific <see cref="AutomationPeer"/> implementation.</returns>
  404. protected virtual AutomationPeer OnCreateAutomationPeer()
  405. {
  406. return new NoneAutomationPeer(this);
  407. }
  408. internal AutomationPeer? GetAutomationPeer()
  409. {
  410. VerifyAccess();
  411. return _automationPeer;
  412. }
  413. internal AutomationPeer GetOrCreateAutomationPeer()
  414. {
  415. VerifyAccess();
  416. if (_automationPeer is object)
  417. {
  418. return _automationPeer;
  419. }
  420. _automationPeer = OnCreateAutomationPeer();
  421. return _automationPeer;
  422. }
  423. /// <inheritdoc/>
  424. protected override void OnPointerReleased(PointerReleasedEventArgs e)
  425. {
  426. base.OnPointerReleased(e);
  427. if (e.Source == this
  428. && !e.Handled
  429. && e.InitialPressMouseButton == MouseButton.Right)
  430. {
  431. var args = new ContextRequestedEventArgs(e);
  432. RaiseEvent(args);
  433. e.Handled = args.Handled;
  434. }
  435. }
  436. /// <inheritdoc/>
  437. protected override void OnKeyUp(KeyEventArgs e)
  438. {
  439. base.OnKeyUp(e);
  440. if (e.Source == this
  441. && !e.Handled)
  442. {
  443. var keymap = TopLevel.GetTopLevel(this)?.PlatformSettings?.HotkeyConfiguration.OpenContextMenu;
  444. if (keymap is null)
  445. {
  446. return;
  447. }
  448. var matches = false;
  449. for (var index = 0; index < keymap.Count; index++)
  450. {
  451. var key = keymap[index];
  452. matches |= key.Matches(e);
  453. if (matches)
  454. {
  455. break;
  456. }
  457. }
  458. if (matches)
  459. {
  460. var args = new ContextRequestedEventArgs();
  461. RaiseEvent(args);
  462. e.Handled = args.Handled;
  463. }
  464. }
  465. }
  466. /// <inheritdoc/>
  467. protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
  468. {
  469. base.OnPropertyChanged(change);
  470. if (change.Property == BoundsProperty)
  471. {
  472. var oldValue = change.GetOldValue<Rect>();
  473. var newValue = change.GetNewValue<Rect>();
  474. // Bounds is a Rect with an X/Y Position as well as Height/Width.
  475. // This means it is possible for the Rect to change position but not size.
  476. // Therefore, we want to explicity check only the size and raise an event
  477. // only when that size has changed.
  478. if (newValue.Size != oldValue.Size)
  479. {
  480. var sizeChangedEventArgs = new SizeChangedEventArgs(
  481. SizeChangedEvent,
  482. source: this,
  483. previousSize: new Size(oldValue.Width, oldValue.Height),
  484. newSize: new Size(newValue.Width, newValue.Height));
  485. OnSizeChanged(sizeChangedEventArgs);
  486. }
  487. }
  488. }
  489. // Since we are resetting the dispatcher instance, the callback might never arrive
  490. internal static void ResetLoadedQueueForUnitTests()
  491. {
  492. _loadedQueue.Clear();
  493. _loadedProcessingQueue.Clear();
  494. _isLoadedProcessing = false;
  495. }
  496. private enum LoadState : byte
  497. {
  498. Unloaded,
  499. Loaded,
  500. LoadPending
  501. }
  502. }
  503. }