InputElement.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using Avalonia.Controls;
  7. using Avalonia.Data;
  8. using Avalonia.Input.GestureRecognizers;
  9. using Avalonia.Interactivity;
  10. using Avalonia.VisualTree;
  11. namespace Avalonia.Input
  12. {
  13. /// <summary>
  14. /// Implements input-related functionality for a control.
  15. /// </summary>
  16. public class InputElement : Interactive, IInputElement
  17. {
  18. /// <summary>
  19. /// Defines the <see cref="Focusable"/> property.
  20. /// </summary>
  21. public static readonly StyledProperty<bool> FocusableProperty =
  22. AvaloniaProperty.Register<InputElement, bool>(nameof(Focusable));
  23. /// <summary>
  24. /// Defines the <see cref="IsEnabled"/> property.
  25. /// </summary>
  26. public static readonly StyledProperty<bool> IsEnabledProperty =
  27. AvaloniaProperty.Register<InputElement, bool>(nameof(IsEnabled), true);
  28. /// <summary>
  29. /// Defines the <see cref="IsEffectivelyEnabled"/> property.
  30. /// </summary>
  31. public static readonly DirectProperty<InputElement, bool> IsEffectivelyEnabledProperty =
  32. AvaloniaProperty.RegisterDirect<InputElement, bool>(
  33. nameof(IsEffectivelyEnabled),
  34. o => o.IsEffectivelyEnabled);
  35. /// <summary>
  36. /// Gets or sets associated mouse cursor.
  37. /// </summary>
  38. public static readonly StyledProperty<Cursor> CursorProperty =
  39. AvaloniaProperty.Register<InputElement, Cursor>(nameof(Cursor), null, true);
  40. /// <summary>
  41. /// Defines the <see cref="IsFocused"/> property.
  42. /// </summary>
  43. public static readonly DirectProperty<InputElement, bool> IsFocusedProperty =
  44. AvaloniaProperty.RegisterDirect<InputElement, bool>(nameof(IsFocused), o => o.IsFocused);
  45. /// <summary>
  46. /// Defines the <see cref="IsHitTestVisible"/> property.
  47. /// </summary>
  48. public static readonly StyledProperty<bool> IsHitTestVisibleProperty =
  49. AvaloniaProperty.Register<InputElement, bool>(nameof(IsHitTestVisible), true);
  50. /// <summary>
  51. /// Defines the <see cref="IsPointerOver"/> property.
  52. /// </summary>
  53. public static readonly DirectProperty<InputElement, bool> IsPointerOverProperty =
  54. AvaloniaProperty.RegisterDirect<InputElement, bool>(nameof(IsPointerOver), o => o.IsPointerOver);
  55. /// <summary>
  56. /// Defines the <see cref="GotFocus"/> event.
  57. /// </summary>
  58. public static readonly RoutedEvent<GotFocusEventArgs> GotFocusEvent =
  59. RoutedEvent.Register<InputElement, GotFocusEventArgs>(nameof(GotFocus), RoutingStrategies.Bubble);
  60. /// <summary>
  61. /// Defines the <see cref="LostFocus"/> event.
  62. /// </summary>
  63. public static readonly RoutedEvent<RoutedEventArgs> LostFocusEvent =
  64. RoutedEvent.Register<InputElement, RoutedEventArgs>(nameof(LostFocus), RoutingStrategies.Bubble);
  65. /// <summary>
  66. /// Defines the <see cref="KeyDown"/> event.
  67. /// </summary>
  68. public static readonly RoutedEvent<KeyEventArgs> KeyDownEvent =
  69. RoutedEvent.Register<InputElement, KeyEventArgs>(
  70. "KeyDown",
  71. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  72. /// <summary>
  73. /// Defines the <see cref="KeyUp"/> event.
  74. /// </summary>
  75. public static readonly RoutedEvent<KeyEventArgs> KeyUpEvent =
  76. RoutedEvent.Register<InputElement, KeyEventArgs>(
  77. "KeyUp",
  78. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  79. /// <summary>
  80. /// Defines the <see cref="TextInput"/> event.
  81. /// </summary>
  82. public static readonly RoutedEvent<TextInputEventArgs> TextInputEvent =
  83. RoutedEvent.Register<InputElement, TextInputEventArgs>(
  84. "TextInput",
  85. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  86. /// <summary>
  87. /// Defines the <see cref="PointerEnter"/> event.
  88. /// </summary>
  89. public static readonly RoutedEvent<PointerEventArgs> PointerEnterEvent =
  90. RoutedEvent.Register<InputElement, PointerEventArgs>(nameof(PointerEnter), RoutingStrategies.Direct);
  91. /// <summary>
  92. /// Defines the <see cref="PointerLeave"/> event.
  93. /// </summary>
  94. public static readonly RoutedEvent<PointerEventArgs> PointerLeaveEvent =
  95. RoutedEvent.Register<InputElement, PointerEventArgs>(nameof(PointerLeave), RoutingStrategies.Direct);
  96. /// <summary>
  97. /// Defines the <see cref="PointerMoved"/> event.
  98. /// </summary>
  99. public static readonly RoutedEvent<PointerEventArgs> PointerMovedEvent =
  100. RoutedEvent.Register<InputElement, PointerEventArgs>(
  101. "PointerMove",
  102. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  103. /// <summary>
  104. /// Defines the <see cref="PointerPressed"/> event.
  105. /// </summary>
  106. public static readonly RoutedEvent<PointerPressedEventArgs> PointerPressedEvent =
  107. RoutedEvent.Register<InputElement, PointerPressedEventArgs>(
  108. "PointerPressed",
  109. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  110. /// <summary>
  111. /// Defines the <see cref="PointerReleased"/> event.
  112. /// </summary>
  113. public static readonly RoutedEvent<PointerReleasedEventArgs> PointerReleasedEvent =
  114. RoutedEvent.Register<InputElement, PointerReleasedEventArgs>(
  115. "PointerReleased",
  116. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  117. /// <summary>
  118. /// Defines the <see cref="PointerCaptureLost"/> routed event.
  119. /// </summary>
  120. public static readonly RoutedEvent<PointerCaptureLostEventArgs> PointerCaptureLostEvent =
  121. RoutedEvent.Register<InputElement, PointerCaptureLostEventArgs>(
  122. "PointerCaptureLost",
  123. RoutingStrategies.Direct);
  124. /// <summary>
  125. /// Defines the <see cref="PointerWheelChanged"/> event.
  126. /// </summary>
  127. public static readonly RoutedEvent<PointerWheelEventArgs> PointerWheelChangedEvent =
  128. RoutedEvent.Register<InputElement, PointerWheelEventArgs>(
  129. "PointerWheelChanged",
  130. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  131. /// <summary>
  132. /// Defines the <see cref="Tapped"/> event.
  133. /// </summary>
  134. public static readonly RoutedEvent<RoutedEventArgs> TappedEvent = Gestures.TappedEvent;
  135. /// <summary>
  136. /// Defines the <see cref="DoubleTapped"/> event.
  137. /// </summary>
  138. public static readonly RoutedEvent<RoutedEventArgs> DoubleTappedEvent = Gestures.DoubleTappedEvent;
  139. private bool _isEffectivelyEnabled = true;
  140. private bool _isFocused;
  141. private bool _isPointerOver;
  142. private GestureRecognizerCollection _gestureRecognizers;
  143. /// <summary>
  144. /// Initializes static members of the <see cref="InputElement"/> class.
  145. /// </summary>
  146. static InputElement()
  147. {
  148. IsEnabledProperty.Changed.Subscribe(IsEnabledChanged);
  149. GotFocusEvent.AddClassHandler<InputElement>((x, e) => x.OnGotFocus(e));
  150. LostFocusEvent.AddClassHandler<InputElement>((x, e) => x.OnLostFocus(e));
  151. KeyDownEvent.AddClassHandler<InputElement>((x, e) => x.OnKeyDown(e));
  152. KeyUpEvent.AddClassHandler<InputElement>((x, e) => x.OnKeyUp(e));
  153. TextInputEvent.AddClassHandler<InputElement>((x, e) => x.OnTextInput(e));
  154. PointerEnterEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerEnterCore(e));
  155. PointerLeaveEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerLeaveCore(e));
  156. PointerMovedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerMoved(e));
  157. PointerPressedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerPressed(e));
  158. PointerReleasedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerReleased(e));
  159. PointerCaptureLostEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerCaptureLost(e));
  160. PointerWheelChangedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerWheelChanged(e));
  161. }
  162. public InputElement()
  163. {
  164. UpdatePseudoClasses(IsFocused, IsPointerOver);
  165. }
  166. /// <summary>
  167. /// Occurs when the control receives focus.
  168. /// </summary>
  169. public event EventHandler<GotFocusEventArgs> GotFocus
  170. {
  171. add { AddHandler(GotFocusEvent, value); }
  172. remove { RemoveHandler(GotFocusEvent, value); }
  173. }
  174. /// <summary>
  175. /// Occurs when the control loses focus.
  176. /// </summary>
  177. public event EventHandler<RoutedEventArgs> LostFocus
  178. {
  179. add { AddHandler(LostFocusEvent, value); }
  180. remove { RemoveHandler(LostFocusEvent, value); }
  181. }
  182. /// <summary>
  183. /// Occurs when a key is pressed while the control has focus.
  184. /// </summary>
  185. public event EventHandler<KeyEventArgs> KeyDown
  186. {
  187. add { AddHandler(KeyDownEvent, value); }
  188. remove { RemoveHandler(KeyDownEvent, value); }
  189. }
  190. /// <summary>
  191. /// Occurs when a key is released while the control has focus.
  192. /// </summary>
  193. public event EventHandler<KeyEventArgs> KeyUp
  194. {
  195. add { AddHandler(KeyUpEvent, value); }
  196. remove { RemoveHandler(KeyUpEvent, value); }
  197. }
  198. /// <summary>
  199. /// Occurs when a user typed some text while the control has focus.
  200. /// </summary>
  201. public event EventHandler<TextInputEventArgs> TextInput
  202. {
  203. add { AddHandler(TextInputEvent, value); }
  204. remove { RemoveHandler(TextInputEvent, value); }
  205. }
  206. /// <summary>
  207. /// Occurs when the pointer enters the control.
  208. /// </summary>
  209. public event EventHandler<PointerEventArgs> PointerEnter
  210. {
  211. add { AddHandler(PointerEnterEvent, value); }
  212. remove { RemoveHandler(PointerEnterEvent, value); }
  213. }
  214. /// <summary>
  215. /// Occurs when the pointer leaves the control.
  216. /// </summary>
  217. public event EventHandler<PointerEventArgs> PointerLeave
  218. {
  219. add { AddHandler(PointerLeaveEvent, value); }
  220. remove { RemoveHandler(PointerLeaveEvent, value); }
  221. }
  222. /// <summary>
  223. /// Occurs when the pointer moves over the control.
  224. /// </summary>
  225. public event EventHandler<PointerEventArgs> PointerMoved
  226. {
  227. add { AddHandler(PointerMovedEvent, value); }
  228. remove { RemoveHandler(PointerMovedEvent, value); }
  229. }
  230. /// <summary>
  231. /// Occurs when the pointer is pressed over the control.
  232. /// </summary>
  233. public event EventHandler<PointerPressedEventArgs> PointerPressed
  234. {
  235. add { AddHandler(PointerPressedEvent, value); }
  236. remove { RemoveHandler(PointerPressedEvent, value); }
  237. }
  238. /// <summary>
  239. /// Occurs when the pointer is released over the control.
  240. /// </summary>
  241. public event EventHandler<PointerReleasedEventArgs> PointerReleased
  242. {
  243. add { AddHandler(PointerReleasedEvent, value); }
  244. remove { RemoveHandler(PointerReleasedEvent, value); }
  245. }
  246. /// <summary>
  247. /// Occurs when the control or its child control loses the pointer capture for any reason,
  248. /// event will not be triggered for a parent control if capture was transferred to another child of that parent control
  249. /// </summary>
  250. public event EventHandler<PointerCaptureLostEventArgs> PointerCaptureLost
  251. {
  252. add => AddHandler(PointerCaptureLostEvent, value);
  253. remove => RemoveHandler(PointerCaptureLostEvent, value);
  254. }
  255. /// <summary>
  256. /// Occurs when the mouse wheen is scrolled over the control.
  257. /// </summary>
  258. public event EventHandler<PointerWheelEventArgs> PointerWheelChanged
  259. {
  260. add { AddHandler(PointerWheelChangedEvent, value); }
  261. remove { RemoveHandler(PointerWheelChangedEvent, value); }
  262. }
  263. /// <summary>
  264. /// Occurs when a tap gesture occurs on the control.
  265. /// </summary>
  266. public event EventHandler<RoutedEventArgs> Tapped
  267. {
  268. add { AddHandler(TappedEvent, value); }
  269. remove { RemoveHandler(TappedEvent, value); }
  270. }
  271. /// <summary>
  272. /// Occurs when a double-tap gesture occurs on the control.
  273. /// </summary>
  274. public event EventHandler<RoutedEventArgs> DoubleTapped
  275. {
  276. add { AddHandler(DoubleTappedEvent, value); }
  277. remove { RemoveHandler(DoubleTappedEvent, value); }
  278. }
  279. /// <summary>
  280. /// Gets or sets a value indicating whether the control can receive focus.
  281. /// </summary>
  282. public bool Focusable
  283. {
  284. get { return GetValue(FocusableProperty); }
  285. set { SetValue(FocusableProperty, value); }
  286. }
  287. /// <summary>
  288. /// Gets or sets a value indicating whether the control is enabled for user interaction.
  289. /// </summary>
  290. public bool IsEnabled
  291. {
  292. get { return GetValue(IsEnabledProperty); }
  293. set { SetValue(IsEnabledProperty, value); }
  294. }
  295. /// <summary>
  296. /// Gets or sets associated mouse cursor.
  297. /// </summary>
  298. public Cursor Cursor
  299. {
  300. get { return GetValue(CursorProperty); }
  301. set { SetValue(CursorProperty, value); }
  302. }
  303. /// <summary>
  304. /// Gets a value indicating whether the control is focused.
  305. /// </summary>
  306. public bool IsFocused
  307. {
  308. get { return _isFocused; }
  309. private set { SetAndRaise(IsFocusedProperty, ref _isFocused, value); }
  310. }
  311. /// <summary>
  312. /// Gets or sets a value indicating whether the control is considered for hit testing.
  313. /// </summary>
  314. public bool IsHitTestVisible
  315. {
  316. get { return GetValue(IsHitTestVisibleProperty); }
  317. set { SetValue(IsHitTestVisibleProperty, value); }
  318. }
  319. /// <summary>
  320. /// Gets a value indicating whether the pointer is currently over the control.
  321. /// </summary>
  322. public bool IsPointerOver
  323. {
  324. get { return _isPointerOver; }
  325. internal set { SetAndRaise(IsPointerOverProperty, ref _isPointerOver, value); }
  326. }
  327. /// <inheritdoc/>
  328. public bool IsEffectivelyEnabled
  329. {
  330. get => _isEffectivelyEnabled;
  331. private set
  332. {
  333. SetAndRaise(IsEffectivelyEnabledProperty, ref _isEffectivelyEnabled, value);
  334. PseudoClasses.Set(":disabled", !value);
  335. }
  336. }
  337. public List<KeyBinding> KeyBindings { get; } = new List<KeyBinding>();
  338. /// <summary>
  339. /// Allows a derived class to override the enabled state of the control.
  340. /// </summary>
  341. /// <remarks>
  342. /// Derived controls may wish to disable the enabled state of the control without overwriting the
  343. /// user-supplied <see cref="IsEnabled"/> setting. This can be done by overriding this property
  344. /// to return the overridden enabled state. If the value returned from <see cref="IsEnabledCore"/>
  345. /// should change, then the derived control should call <see cref="UpdateIsEffectivelyEnabled()"/>.
  346. /// </remarks>
  347. protected virtual bool IsEnabledCore => IsEnabled;
  348. public GestureRecognizerCollection GestureRecognizers
  349. => _gestureRecognizers ?? (_gestureRecognizers = new GestureRecognizerCollection(this));
  350. /// <summary>
  351. /// Focuses the control.
  352. /// </summary>
  353. public void Focus()
  354. {
  355. FocusManager.Instance?.Focus(this);
  356. }
  357. /// <inheritdoc/>
  358. protected override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
  359. {
  360. base.OnDetachedFromVisualTreeCore(e);
  361. if (IsFocused)
  362. {
  363. FocusManager.Instance.Focus(null);
  364. }
  365. }
  366. /// <inheritdoc/>
  367. protected override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
  368. {
  369. base.OnAttachedToVisualTreeCore(e);
  370. UpdateIsEffectivelyEnabled();
  371. }
  372. /// <summary>
  373. /// Called before the <see cref="GotFocus"/> event occurs.
  374. /// </summary>
  375. /// <param name="e">The event args.</param>
  376. protected virtual void OnGotFocus(GotFocusEventArgs e)
  377. {
  378. IsFocused = e.Source == this;
  379. }
  380. /// <summary>
  381. /// Called before the <see cref="LostFocus"/> event occurs.
  382. /// </summary>
  383. /// <param name="e">The event args.</param>
  384. protected virtual void OnLostFocus(RoutedEventArgs e)
  385. {
  386. IsFocused = false;
  387. }
  388. /// <summary>
  389. /// Called before the <see cref="KeyDown"/> event occurs.
  390. /// </summary>
  391. /// <param name="e">The event args.</param>
  392. protected virtual void OnKeyDown(KeyEventArgs e)
  393. {
  394. }
  395. /// <summary>
  396. /// Called before the <see cref="KeyUp"/> event occurs.
  397. /// </summary>
  398. /// <param name="e">The event args.</param>
  399. protected virtual void OnKeyUp(KeyEventArgs e)
  400. {
  401. }
  402. /// <summary>
  403. /// Called before the <see cref="TextInput"/> event occurs.
  404. /// </summary>
  405. /// <param name="e">The event args.</param>
  406. protected virtual void OnTextInput(TextInputEventArgs e)
  407. {
  408. }
  409. /// <summary>
  410. /// Called before the <see cref="PointerEnter"/> event occurs.
  411. /// </summary>
  412. /// <param name="e">The event args.</param>
  413. protected virtual void OnPointerEnter(PointerEventArgs e)
  414. {
  415. }
  416. /// <summary>
  417. /// Called before the <see cref="PointerLeave"/> event occurs.
  418. /// </summary>
  419. /// <param name="e">The event args.</param>
  420. protected virtual void OnPointerLeave(PointerEventArgs e)
  421. {
  422. }
  423. /// <summary>
  424. /// Called before the <see cref="PointerMoved"/> event occurs.
  425. /// </summary>
  426. /// <param name="e">The event args.</param>
  427. protected virtual void OnPointerMoved(PointerEventArgs e)
  428. {
  429. if (_gestureRecognizers?.HandlePointerMoved(e) == true)
  430. e.Handled = true;
  431. }
  432. /// <summary>
  433. /// Called before the <see cref="PointerPressed"/> event occurs.
  434. /// </summary>
  435. /// <param name="e">The event args.</param>
  436. protected virtual void OnPointerPressed(PointerPressedEventArgs e)
  437. {
  438. if (_gestureRecognizers?.HandlePointerPressed(e) == true)
  439. e.Handled = true;
  440. }
  441. /// <summary>
  442. /// Called before the <see cref="PointerReleased"/> event occurs.
  443. /// </summary>
  444. /// <param name="e">The event args.</param>
  445. protected virtual void OnPointerReleased(PointerReleasedEventArgs e)
  446. {
  447. if (_gestureRecognizers?.HandlePointerReleased(e) == true)
  448. e.Handled = true;
  449. }
  450. /// <summary>
  451. /// Called before the <see cref="PointerCaptureLost"/> event occurs.
  452. /// </summary>
  453. /// <param name="e">The event args.</param>
  454. protected virtual void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
  455. {
  456. _gestureRecognizers?.HandlePointerCaptureLost(e);
  457. }
  458. /// <summary>
  459. /// Called before the <see cref="PointerWheelChanged"/> event occurs.
  460. /// </summary>
  461. /// <param name="e">The event args.</param>
  462. protected virtual void OnPointerWheelChanged(PointerWheelEventArgs e)
  463. {
  464. }
  465. protected override void OnPropertyChanged<T>(AvaloniaProperty<T> property, Optional<T> oldValue, BindingValue<T> newValue, BindingPriority priority)
  466. {
  467. base.OnPropertyChanged(property, oldValue, newValue, priority);
  468. if (property == IsFocusedProperty)
  469. {
  470. UpdatePseudoClasses(newValue.GetValueOrDefault<bool>(), null);
  471. }
  472. else if (property == IsPointerOverProperty)
  473. {
  474. UpdatePseudoClasses(null, newValue.GetValueOrDefault<bool>());
  475. }
  476. }
  477. /// <summary>
  478. /// Updates the <see cref="IsEffectivelyEnabled"/> property value according to the parent
  479. /// control's enabled state and <see cref="IsEnabledCore"/>.
  480. /// </summary>
  481. protected void UpdateIsEffectivelyEnabled()
  482. {
  483. UpdateIsEffectivelyEnabled(this.GetVisualParent<InputElement>());
  484. }
  485. private static void IsEnabledChanged(AvaloniaPropertyChangedEventArgs e)
  486. {
  487. ((InputElement)e.Sender).UpdateIsEffectivelyEnabled();
  488. }
  489. /// <summary>
  490. /// Called before the <see cref="PointerEnter"/> event occurs.
  491. /// </summary>
  492. /// <param name="e">The event args.</param>
  493. private void OnPointerEnterCore(PointerEventArgs e)
  494. {
  495. IsPointerOver = true;
  496. OnPointerEnter(e);
  497. }
  498. /// <summary>
  499. /// Called before the <see cref="PointerLeave"/> event occurs.
  500. /// </summary>
  501. /// <param name="e">The event args.</param>
  502. private void OnPointerLeaveCore(PointerEventArgs e)
  503. {
  504. IsPointerOver = false;
  505. OnPointerLeave(e);
  506. }
  507. /// <summary>
  508. /// Updates the <see cref="IsEffectivelyEnabled"/> property based on the parent's
  509. /// <see cref="IsEffectivelyEnabled"/>.
  510. /// </summary>
  511. /// <param name="parent">The parent control.</param>
  512. private void UpdateIsEffectivelyEnabled(InputElement parent)
  513. {
  514. IsEffectivelyEnabled = IsEnabledCore && (parent?.IsEffectivelyEnabled ?? true);
  515. // PERF-SENSITIVE: This is called on entire hierarchy and using foreach or LINQ
  516. // will cause extra allocations and overhead.
  517. var children = VisualChildren;
  518. // ReSharper disable once ForCanBeConvertedToForeach
  519. for (int i = 0; i < children.Count; ++i)
  520. {
  521. var child = children[i] as InputElement;
  522. child?.UpdateIsEffectivelyEnabled(this);
  523. }
  524. }
  525. private void UpdatePseudoClasses(bool? isFocused, bool? isPointerOver)
  526. {
  527. if (isFocused.HasValue)
  528. {
  529. PseudoClasses.Set(":focus", isFocused.Value);
  530. }
  531. if (isPointerOver.HasValue)
  532. {
  533. PseudoClasses.Set(":pointerover", isPointerOver.Value);
  534. }
  535. }
  536. }
  537. }