#nullable enable using System; using System.Collections.Generic; using Avalonia.Controls; using Avalonia.Controls.Metadata; using Avalonia.Input.GestureRecognizers; using Avalonia.Input.TextInput; using Avalonia.Interactivity; using Avalonia.Reactive; using Avalonia.VisualTree; namespace Avalonia.Input { /// /// Implements input-related functionality for a control. /// [PseudoClasses(":disabled", ":focus", ":focus-visible", ":focus-within", ":pointerover")] public class InputElement : Interactive, IInputElement { /// /// Defines the property. /// public static readonly StyledProperty FocusableProperty = AvaloniaProperty.Register(nameof(Focusable)); /// /// Defines the property. /// public static readonly StyledProperty IsEnabledProperty = AvaloniaProperty.Register(nameof(IsEnabled), true); /// /// Defines the property. /// public static readonly DirectProperty IsEffectivelyEnabledProperty = AvaloniaProperty.RegisterDirect( nameof(IsEffectivelyEnabled), o => o.IsEffectivelyEnabled); /// /// Gets or sets associated mouse cursor. /// public static readonly StyledProperty CursorProperty = AvaloniaProperty.Register(nameof(Cursor), null, true); /// /// Defines the property. /// public static readonly DirectProperty IsKeyboardFocusWithinProperty = AvaloniaProperty.RegisterDirect( nameof(IsKeyboardFocusWithin), o => o.IsKeyboardFocusWithin); /// /// Defines the property. /// public static readonly DirectProperty IsFocusedProperty = AvaloniaProperty.RegisterDirect(nameof(IsFocused), o => o.IsFocused); /// /// Defines the property. /// public static readonly StyledProperty IsHitTestVisibleProperty = AvaloniaProperty.Register(nameof(IsHitTestVisible), true); /// /// Defines the property. /// public static readonly DirectProperty IsPointerOverProperty = AvaloniaProperty.RegisterDirect(nameof(IsPointerOver), o => o.IsPointerOver); /// /// Defines the property. /// public static readonly StyledProperty IsTabStopProperty = KeyboardNavigation.IsTabStopProperty.AddOwner(); /// /// Defines the event. /// public static readonly RoutedEvent GotFocusEvent = RoutedEvent.Register(nameof(GotFocus), RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent GettingFocusEvent = RoutedEvent.Register(nameof(GettingFocus), RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent LostFocusEvent = RoutedEvent.Register(nameof(LostFocus), RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent LosingFocusEvent = RoutedEvent.Register(nameof(LosingFocus), RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent KeyDownEvent = RoutedEvent.Register( nameof(KeyDown), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent KeyUpEvent = RoutedEvent.Register( nameof(KeyUp), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the property. /// public static readonly StyledProperty TabIndexProperty = KeyboardNavigation.TabIndexProperty.AddOwner(); /// /// Defines the event. /// public static readonly RoutedEvent TextInputEvent = RoutedEvent.Register( nameof(TextInput), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent TextInputMethodClientRequestedEvent = RoutedEvent.Register( nameof(TextInputMethodClientRequested), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent PointerEnteredEvent = RoutedEvent.Register( nameof(PointerEntered), RoutingStrategies.Direct); /// /// Defines the event. /// public static readonly RoutedEvent PointerExitedEvent = RoutedEvent.Register( nameof(PointerExited), RoutingStrategies.Direct); /// /// Defines the event. /// public static readonly RoutedEvent PointerMovedEvent = RoutedEvent.Register( nameof(PointerMoved), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent PointerPressedEvent = RoutedEvent.Register( nameof(PointerPressed), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent PointerReleasedEvent = RoutedEvent.Register( nameof(PointerReleased), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the routed event. /// public static readonly RoutedEvent PointerCaptureLostEvent = RoutedEvent.Register( nameof(PointerCaptureLost), RoutingStrategies.Direct); /// /// Defines the event. /// public static readonly RoutedEvent PointerWheelChangedEvent = RoutedEvent.Register( nameof(PointerWheelChanged), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent TappedEvent = Gestures.TappedEvent; /// /// Defines the event. /// public static readonly RoutedEvent RightTappedEvent = Gestures.RightTappedEvent; /// /// Defines the event. /// public static readonly RoutedEvent HoldingEvent = Gestures.HoldingEvent; /// /// Defines the event. /// public static readonly RoutedEvent DoubleTappedEvent = Gestures.DoubleTappedEvent; private bool _isEffectivelyEnabled = true; private bool _isFocused; private bool _isKeyboardFocusWithin; private bool _isFocusVisible; private bool _isPointerOver; private GestureRecognizerCollection? _gestureRecognizers; /// /// Initializes static members of the class. /// static InputElement() { IsEnabledProperty.Changed.Subscribe(IsEnabledChanged); GotFocusEvent.AddClassHandler((x, e) => x.OnGotFocusCore(e)); LostFocusEvent.AddClassHandler((x, e) => x.OnLostFocusCore(e)); GettingFocusEvent.AddClassHandler((x, e) => x.OnGettingFocus(e)); LosingFocusEvent.AddClassHandler((x, e) => x.OnLosingFocus(e)); KeyDownEvent.AddClassHandler((x, e) => x.OnKeyDown(e)); KeyUpEvent.AddClassHandler((x, e) => x.OnKeyUp(e)); TextInputEvent.AddClassHandler((x, e) => x.OnTextInput(e)); PointerEnteredEvent.AddClassHandler((x, e) => x.OnPointerEnteredCore(e)); PointerExitedEvent.AddClassHandler((x, e) => x.OnPointerExitedCore(e)); PointerMovedEvent.AddClassHandler((x, e) => x.OnPointerMoved(e)); PointerPressedEvent.AddClassHandler((x, e) => x.OnPointerPressed(e)); PointerReleasedEvent.AddClassHandler((x, e) => x.OnPointerReleased(e)); PointerCaptureLostEvent.AddClassHandler((x, e) => x.OnPointerCaptureLost(e)); PointerWheelChangedEvent.AddClassHandler((x, e) => x.OnPointerWheelChanged(e)); TappedEvent.AddClassHandler((x, e) => x.OnTapped(e)); RightTappedEvent.AddClassHandler((x, e) => x.OnRightTapped(e)); DoubleTappedEvent.AddClassHandler((x, e) => x.OnDoubleTapped(e)); HoldingEvent.AddClassHandler((x, e) => x.OnHolding(e)); // Gesture only handlers PointerMovedEvent.AddClassHandler((x, e) => x.OnGesturePointerMoved(e), handledEventsToo: true); PointerPressedEvent.AddClassHandler((x, e) => x.OnGesturePointerPressed(e), handledEventsToo: true); PointerReleasedEvent.AddClassHandler((x, e) => x.OnGesturePointerReleased(e), handledEventsToo: true); PointerCaptureLostEvent.AddClassHandler((x, e) => x.OnGesturePointerCaptureLost(e), handledEventsToo: true); // Access Key Handling AccessKeyHandler.AccessKeyEvent.AddClassHandler((x, e) => x.OnAccessKey(e)); } public InputElement() { UpdatePseudoClasses(IsFocused, IsPointerOver); } /// /// Occurs when the control receives focus. /// public event EventHandler? GotFocus { add { AddHandler(GotFocusEvent, value); } remove { RemoveHandler(GotFocusEvent, value); } } /// /// Occurs before the control receives focus. /// public event EventHandler? GettingFocus { add { AddHandler(GettingFocusEvent, value); } remove { RemoveHandler(GettingFocusEvent, value); } } /// /// Occurs when the control loses focus. /// public event EventHandler? LostFocus { add { AddHandler(LostFocusEvent, value); } remove { RemoveHandler(LostFocusEvent, value); } } /// /// Occurs before the control loses focus. /// public event EventHandler? LosingFocus { add { AddHandler(LosingFocusEvent, value); } remove { RemoveHandler(LosingFocusEvent, value); } } /// /// Occurs when a key is pressed while the control has focus. /// public event EventHandler? KeyDown { add { AddHandler(KeyDownEvent, value); } remove { RemoveHandler(KeyDownEvent, value); } } /// /// Occurs when a key is released while the control has focus. /// public event EventHandler? KeyUp { add { AddHandler(KeyUpEvent, value); } remove { RemoveHandler(KeyUpEvent, value); } } /// /// Occurs when a user typed some text while the control has focus. /// public event EventHandler? TextInput { add { AddHandler(TextInputEvent, value); } remove { RemoveHandler(TextInputEvent, value); } } /// /// Occurs when an input element gains input focus and input method is looking for the corresponding client /// public event EventHandler? TextInputMethodClientRequested { add { AddHandler(TextInputMethodClientRequestedEvent, value); } remove { RemoveHandler(TextInputMethodClientRequestedEvent, value); } } /// /// Occurs when the pointer enters the control. /// public event EventHandler? PointerEntered { add { AddHandler(PointerEnteredEvent, value); } remove { RemoveHandler(PointerEnteredEvent, value); } } /// /// Occurs when the pointer leaves the control. /// public event EventHandler? PointerExited { add { AddHandler(PointerExitedEvent, value); } remove { RemoveHandler(PointerExitedEvent, value); } } /// /// Occurs when the pointer moves over the control. /// public event EventHandler? PointerMoved { add { AddHandler(PointerMovedEvent, value); } remove { RemoveHandler(PointerMovedEvent, value); } } /// /// Occurs when the pointer is pressed over the control. /// public event EventHandler? PointerPressed { add { AddHandler(PointerPressedEvent, value); } remove { RemoveHandler(PointerPressedEvent, value); } } /// /// Occurs when the pointer is released over the control. /// public event EventHandler? PointerReleased { add { AddHandler(PointerReleasedEvent, value); } remove { RemoveHandler(PointerReleasedEvent, value); } } /// /// Occurs when the control or its child control loses the pointer capture for any reason, /// event will not be triggered for a parent control if capture was transferred to another child of that parent control /// public event EventHandler? PointerCaptureLost { add => AddHandler(PointerCaptureLostEvent, value); remove => RemoveHandler(PointerCaptureLostEvent, value); } /// /// Occurs when the mouse is scrolled over the control. /// public event EventHandler? PointerWheelChanged { add { AddHandler(PointerWheelChangedEvent, value); } remove { RemoveHandler(PointerWheelChangedEvent, value); } } /// /// Occurs when a tap gesture occurs on the control. /// public event EventHandler? Tapped { add { AddHandler(TappedEvent, value); } remove { RemoveHandler(TappedEvent, value); } } /// /// Occurs when a right tap gesture occurs on the control. /// public event EventHandler? RightTapped { add { AddHandler(RightTappedEvent, value); } remove { RemoveHandler(RightTappedEvent, value); } } /// /// Occurs when a hold gesture occurs on the control. /// public event EventHandler? Holding { add { AddHandler(HoldingEvent, value); } remove { RemoveHandler(HoldingEvent, value); } } /// /// Occurs when a double-tap gesture occurs on the control. /// public event EventHandler? DoubleTapped { add { AddHandler(DoubleTappedEvent, value); } remove { RemoveHandler(DoubleTappedEvent, value); } } /// /// Gets or sets a value indicating whether the control can receive focus. /// public bool Focusable { get { return GetValue(FocusableProperty); } set { SetValue(FocusableProperty, value); } } /// /// Gets or sets a value indicating whether the control is enabled for user interaction. /// public bool IsEnabled { get { return GetValue(IsEnabledProperty); } set { SetValue(IsEnabledProperty, value); } } /// /// Gets or sets associated mouse cursor. /// public Cursor? Cursor { get { return GetValue(CursorProperty); } set { SetValue(CursorProperty, value); } } /// /// Gets a value indicating whether keyboard focus is anywhere within the element or its visual tree child elements. /// public bool IsKeyboardFocusWithin { get => _isKeyboardFocusWithin; internal set => SetAndRaise(IsKeyboardFocusWithinProperty, ref _isKeyboardFocusWithin, value); } /// /// Gets a value indicating whether the control is focused. /// public bool IsFocused { get { return _isFocused; } private set { SetAndRaise(IsFocusedProperty, ref _isFocused, value); } } /// /// Gets or sets a value indicating whether the control is considered for hit testing. /// public bool IsHitTestVisible { get { return GetValue(IsHitTestVisibleProperty); } set { SetValue(IsHitTestVisibleProperty, value); } } /// /// Gets a value indicating whether the pointer is currently over the control. /// public bool IsPointerOver { get { return _isPointerOver; } internal set { SetAndRaise(IsPointerOverProperty, ref _isPointerOver, value); } } /// /// Gets or sets a value that indicates whether the control is included in tab navigation. /// public bool IsTabStop { get => GetValue(IsTabStopProperty); set => SetValue(IsTabStopProperty, value); } /// public bool IsEffectivelyEnabled { get => _isEffectivelyEnabled; private set { SetAndRaise(IsEffectivelyEnabledProperty, ref _isEffectivelyEnabled, value); PseudoClasses.Set(":disabled", !value); if (!IsEffectivelyEnabled && FocusManager.GetFocusManager(this) is { } focusManager && Equals(focusManager.GetFocusedElement(), this)) { focusManager.ClearFocus(); } } } /// /// Gets or sets a value that determines the order in which elements receive focus when the /// user navigates through controls by pressing the Tab key. /// public int TabIndex { get => GetValue(TabIndexProperty); set => SetValue(TabIndexProperty, value); } public List KeyBindings { get; } = new List(); /// /// Allows a derived class to override the enabled state of the control. /// /// /// Derived controls may wish to disable the enabled state of the control without overwriting the /// user-supplied setting. This can be done by overriding this property /// to return the overridden enabled state. If the value returned from /// should change, then the derived control should call . /// protected virtual bool IsEnabledCore => IsEnabled; public GestureRecognizerCollection GestureRecognizers => _gestureRecognizers ?? (_gestureRecognizers = new GestureRecognizerCollection(this)); /// public bool Focus(NavigationMethod method = NavigationMethod.Unspecified, KeyModifiers keyModifiers = KeyModifiers.None) { return FocusManager.GetFocusManager(this)?.Focus(this, method, keyModifiers) ?? false; } /// protected override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTreeCore(e); if (IsFocused) { FocusManager.GetFocusManager(e.Root as IInputElement)?.ClearFocusOnElementRemoved(this, e.Parent); } IsKeyboardFocusWithin = false; } /// /// This method is used to execute the action on an effective IInputElement when a corresponding access key has been invoked. /// By default, the Focus() method is invoked with the NavigationMethod.Tab to indicate a visual focus adorner. /// Overwrite this method if other methods or additional functionality is needed when an item should receive the focus. /// /// AccessKeyEventArgs are passed on to indicate if there are multiple matches or not. protected virtual void OnAccessKey(RoutedEventArgs e) { Focus(NavigationMethod.Tab); } /// protected override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTreeCore(e); UpdateIsEffectivelyEnabled(); } private void OnGotFocusCore(GotFocusEventArgs e) { var isFocused = e.Source == this; _isFocusVisible = isFocused && (e.NavigationMethod == NavigationMethod.Directional || e.NavigationMethod == NavigationMethod.Tab); IsFocused = isFocused; OnGotFocus(e); } protected virtual void OnGettingFocus(FocusChangingEventArgs e) { } protected virtual void OnLosingFocus(FocusChangingEventArgs e) { } /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnGotFocus(GotFocusEventArgs e) { } private void OnLostFocusCore(RoutedEventArgs e) { _isFocusVisible = false; IsFocused = false; OnLostFocus(e); } /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnLostFocus(RoutedEventArgs e) { } /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnKeyDown(KeyEventArgs e) { } /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnKeyUp(KeyEventArgs e) { } /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnTextInput(TextInputEventArgs e) { } /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnPointerEntered(PointerEventArgs e) { } /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnPointerExited(PointerEventArgs e) { } /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnPointerMoved(PointerEventArgs e) { } /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnPointerPressed(PointerPressedEventArgs e) { } /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnPointerReleased(PointerReleasedEventArgs e) { } private void OnGesturePointerReleased(PointerReleasedEventArgs e) { if (!e.IsGestureRecognitionSkipped) if (_gestureRecognizers?.HandlePointerReleased(e) == true) { e.Handled = true; } } private void OnGesturePointerCaptureLost(PointerCaptureLostEventArgs e) { _gestureRecognizers?.HandleCaptureLost(e.Pointer); } private void OnGesturePointerPressed(PointerPressedEventArgs e) { if (!e.IsGestureRecognitionSkipped) if (_gestureRecognizers?.HandlePointerPressed(e) == true) { e.Handled = true; } } private void OnGesturePointerMoved(PointerEventArgs e) { if (!e.IsGestureRecognitionSkipped) if (_gestureRecognizers?.HandlePointerMoved(e) == true) { e.Handled = true; } } /// /// Called when FocusManager get the next TabStop to interact with the focused control. /// /// Next tab stop. protected internal virtual InputElement? GetNextTabStopOverride() => null; /// /// Called when FocusManager get the previous TabStop to interact with the focused control. /// /// Previous tab stop. protected internal virtual InputElement? GetPreviousTabStopOverride() => null; /// /// Called when FocusManager is looking for the first focusable element from the specified search scope. /// /// First focusable element if available. protected internal virtual InputElement? GetFirstFocusableElementOverride() => null; /// /// Called when FocusManager is looking for the last focusable element from the specified search scope. /// /// Last focusable element if available/>. protected internal virtual InputElement? GetLastFocusableElementOverride() => null; /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnPointerCaptureLost(PointerCaptureLostEventArgs e) { } internal static bool ProcessTabStop(IInputElement? contentRoot, IInputElement? focusedElement, IInputElement? candidateTabStopElement, bool isReverse, bool didCycleFocusAtRootVisual, out IInputElement? newTabStop) { newTabStop = null; bool isTabStopOverridden = false; bool isCandidateTabStopOverridden = false; IInputElement? currentFocusedTarget = focusedElement; InputElement? focusedTargetAsIE = focusedElement as InputElement; InputElement? candidateTargetAsIE = candidateTabStopElement as InputElement; InputElement? newCandidateTargetAsIE = null; IInputElement? newCandidateTabStop = null; IInputElement? spNewTabStop = null; if (focusedTargetAsIE != null) { isTabStopOverridden = focusedTargetAsIE.ProcessTabStopInternal(candidateTabStopElement, isReverse, didCycleFocusAtRootVisual, out spNewTabStop); } if (!isTabStopOverridden && candidateTargetAsIE != null) { isTabStopOverridden = candidateTargetAsIE.ProcessCandidateTabStopInternal(focusedElement, null, isReverse, out spNewTabStop); } else if (isTabStopOverridden && newTabStop != null) { newCandidateTargetAsIE = spNewTabStop as InputElement; if (newCandidateTargetAsIE != null) { isCandidateTabStopOverridden = newCandidateTargetAsIE.ProcessCandidateTabStopInternal(focusedElement, spNewTabStop, isReverse, out newCandidateTabStop); } } if (isCandidateTabStopOverridden) { if (newCandidateTabStop != null) { newTabStop = newCandidateTabStop; } isTabStopOverridden = true; } else if (isTabStopOverridden) { if (newTabStop != null) { newTabStop = spNewTabStop; } isTabStopOverridden = true; } return isTabStopOverridden; } private bool ProcessTabStopInternal(IInputElement? candidateTabStopElement, bool isReverse, bool didCycleFocusAtRootVisual, out IInputElement? newTabStop) { InputElement? current = this; newTabStop = null; var candidateTabStopOverridden = false; while (current != null && !candidateTabStopOverridden) { candidateTabStopOverridden = current.ProcessTabStopOverride(this, candidateTabStopElement, isReverse, didCycleFocusAtRootVisual, ref newTabStop); current = (current as Visual)?.Parent as InputElement; } return candidateTabStopOverridden; } private bool ProcessCandidateTabStopInternal(IInputElement? currentTabStop, IInputElement? overridenCandidateTabStopElement, bool isReverse, out IInputElement? newTabStop) { InputElement? current = this; newTabStop = null; var candidateTabStopOverridden = false; while (current != null && !candidateTabStopOverridden) { candidateTabStopOverridden = current.ProcessCandidateTabStopOverride(currentTabStop, this, overridenCandidateTabStopElement, isReverse, ref newTabStop); current = (current as Visual)?.Parent as InputElement; } return candidateTabStopOverridden; } protected internal virtual bool ProcessTabStopOverride(IInputElement? focusedElement, IInputElement? candidateTabStopElement, bool isReverse, bool didCycleFocusAtRootVisual, ref IInputElement? newTabStop) { return false; } protected internal virtual bool ProcessCandidateTabStopOverride(IInputElement? focusedElement, IInputElement? candidateTabStopElement, IInputElement? overridenCandidateTabStopElement, bool isReverse, ref IInputElement? newTabStop) { return false; } /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnPointerWheelChanged(PointerWheelEventArgs e) { } /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnTapped(TappedEventArgs e) { } /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnRightTapped(TappedEventArgs e) { } /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnDoubleTapped(TappedEventArgs e) { } /// /// Invoked when an unhandled reaches an element in its /// route that is derived from this class. Implement this method to add class handling /// for this event. /// /// Data about the event. protected virtual void OnHolding(HoldingRoutedEventArgs e) { } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); if (change.Property == IsFocusedProperty) { UpdatePseudoClasses(change.GetNewValue(), null); } else if (change.Property == IsPointerOverProperty) { UpdatePseudoClasses(null, change.GetNewValue()); } else if (change.Property == IsKeyboardFocusWithinProperty) { PseudoClasses.Set(":focus-within", change.GetNewValue()); } else if (change.Property == IsVisibleProperty) { if (!change.GetNewValue() && IsKeyboardFocusWithin && FocusManager.GetFocusManager(this) is { } focusManager) { if (focusManager.GetFocusedElement() is { } focusedElement && VisualParent != null) { focusManager.ClearFocusOnElementRemoved(focusedElement, VisualParent); } else { focusManager.ClearFocus(); } } } } /// /// Updates the property value according to the parent /// control's enabled state and . /// protected void UpdateIsEffectivelyEnabled() { UpdateIsEffectivelyEnabled(this.GetVisualParent()); } private static void IsEnabledChanged(AvaloniaPropertyChangedEventArgs e) { ((InputElement)e.Sender).UpdateIsEffectivelyEnabled(); } /// /// Called before the event occurs. /// /// The event args. private void OnPointerEnteredCore(PointerEventArgs e) { IsPointerOver = true; OnPointerEntered(e); } /// /// Called before the event occurs. /// /// The event args. private void OnPointerExitedCore(PointerEventArgs e) { IsPointerOver = false; OnPointerExited(e); } /// /// Updates the property based on the parent's /// . /// /// The parent control. private void UpdateIsEffectivelyEnabled(InputElement? parent) { IsEffectivelyEnabled = IsEnabledCore && (parent?.IsEffectivelyEnabled ?? true); // PERF-SENSITIVE: This is called on entire hierarchy and using foreach or LINQ // will cause extra allocations and overhead. var children = VisualChildren; // ReSharper disable once ForCanBeConvertedToForeach for (int i = 0; i < children.Count; ++i) { var child = children[i] as InputElement; child?.UpdateIsEffectivelyEnabled(this); } } private void UpdatePseudoClasses(bool? isFocused, bool? isPointerOver) { if (isFocused.HasValue) { PseudoClasses.Set(":focus", isFocused.Value); PseudoClasses.Set(":focus-visible", _isFocusVisible); } if (isPointerOver.HasValue) { PseudoClasses.Set(":pointerover", isPointerOver.Value); } } } }