diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs index f4132a5ced..32d5d9f648 100644 --- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs @@ -212,8 +212,6 @@ namespace Avalonia.Controls private bool _itemTemplateIsFromValueMemberBinding = true; private bool _settingItemTemplateFromValueMemberBinding; - private bool _isFocused = false; - private string? _searchText = string.Empty; private readonly EventHandler _populateDropDownHandler; @@ -751,28 +749,34 @@ namespace Avalonia.Controls } } - /// - /// Provides handling for the - /// event. - /// - /// A - /// that contains the event data. + /// protected override void OnGotFocus(FocusChangedEventArgs e) { + if (TextBox is not null && TextBoxSelectionLength <= 0) + { + TextBox.Focus(); + TextBox.SelectAll(); + } + base.OnGotFocus(e); - FocusChanged(HasFocus()); } - /// - /// Provides handling for the - /// event. - /// - /// A - /// that contains the event data. + /// protected override void OnLostFocus(FocusChangedEventArgs e) { + SetCurrentValue(IsDropDownOpenProperty, false); + + _userCalledPopulate = false; + + var textBoxContextMenuIsOpen = TextBox?.ContextFlyout?.IsOpen == true || TextBox?.ContextMenu?.IsOpen == true; + var contextMenuIsOpen = ContextFlyout?.IsOpen == true || ContextMenu?.IsOpen == true; + + if (!textBoxContextMenuIsOpen && !contextMenuIsOpen && ClearSelectionOnLostFocus) + { + ClearTextBoxSelection(); + } + base.OnLostFocus(e); - FocusChanged(HasFocus()); } protected override AutomationPeer OnCreateAutomationPeer() => new AutoCompleteBoxAutomationPeer(this); @@ -785,79 +789,9 @@ namespace Avalonia.Controls /// true to indicate the /// has focus; /// otherwise, false. + [Obsolete($"Use {nameof(IsKeyboardFocusWithin)} instead.")] protected bool HasFocus() => IsKeyboardFocusWithin; - /// - /// Handles the FocusChanged event. - /// - /// A value indicating whether the control - /// currently has the focus. - private void FocusChanged(bool hasFocus) - { - // The OnGotFocus & OnLostFocus are asynchronously and cannot - // reliably tell you that have the focus. All they do is let you - // know that the focus changed sometime in the past. To determine - // if you currently have the focus you need to do consult the - // FocusManager (see HasFocus()). - - bool wasFocused = _isFocused; - _isFocused = hasFocus; - - if (hasFocus) - { - - if (!wasFocused && TextBox != null && TextBoxSelectionLength <= 0) - { - TextBox.Focus(); - TextBox.SelectAll(); - } - } - else - { - // Check if we still have focus in the parent's focus scope - if (GetFocusScope() is { } scope && - (FocusManager.GetFocusManager(this)?.GetFocusedElement(scope) is not { } focused || - (focused != this && - (focused is Visual v && !this.IsVisualAncestorOf(v))))) - { - SetCurrentValue(IsDropDownOpenProperty, false); - } - - _userCalledPopulate = false; - - var textBoxContextMenuIsOpen = TextBox?.ContextFlyout?.IsOpen == true || TextBox?.ContextMenu?.IsOpen == true; - var contextMenuIsOpen = ContextFlyout?.IsOpen == true || ContextMenu?.IsOpen == true; - - if (!textBoxContextMenuIsOpen && !contextMenuIsOpen && ClearSelectionOnLostFocus) - { - ClearTextBoxSelection(); - } - } - - _isFocused = hasFocus; - - IFocusScope? GetFocusScope() - { - IInputElement? c = this; - - while (c != null) - { - if (c is IFocusScope scope && - c is Visual v && - v.VisualRoot is Visual root && - root.IsVisible) - { - return scope; - } - - c = (c as Visual)?.GetVisualParent() ?? - ((c as IHostedVisualTreeRoot)?.Host as IInputElement); - } - - return null; - } - } - /// /// Occurs asynchronously when the text in the portion of the /// changes. diff --git a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs index 4232624fad..d5bcde0c6d 100644 --- a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs @@ -584,6 +584,40 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Losing_Focus_Closes_DropDown() + { + using var app = UnitTestApplication.Start(FocusServices); + + var target1 = CreateControl(); + target1.ItemsSource = CreateSimpleStringArray(); + var textBox1 = GetTextBox(target1); + + var target2 = CreateControl(); + + target1.ApplyTemplate(); + target2.ApplyTemplate(); + + _ = new TestRoot + { + Child = new StackPanel + { + Children = { target1, target2 } + } + }; + + target1.Focus(); + textBox1.Text = "a"; + Dispatcher.UIThread.RunJobs(null, TestContext.Current.CancellationToken); + Assert.True(target1.IsDropDownOpen); + + target2.Focus(); + Dispatcher.UIThread.RunJobs(null, TestContext.Current.CancellationToken); + + Assert.False(target1.IsFocused); + Assert.False(target1.IsDropDownOpen); + } + /// /// Retrieves a defined predicate filter through a new AutoCompleteBox /// control instance.