Browse Source

Prevent AutoCompleteBox getting stuck in a state where it can't drop down. (#17074)

* Added failing AutoCompleteBox test.

* Make sure that flags are reset if exits early.

Wrap everything in a `try`...`finally` block so that they get reset even on exception.
pull/17122/head
Steven Kirk 1 year ago
committed by GitHub
parent
commit
b8d8fda4ea
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 103
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
  2. 25
      tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs

103
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs

@ -1403,74 +1403,75 @@ namespace Avalonia.Controls
// Indicate that filtering is ongoing
_filterInAction = true;
if (_items == null)
try
{
ClearView();
return;
}
if (_items == null)
{
ClearView();
return;
}
// Cache the current text value
string text = Text ?? string.Empty;
// Cache the current text value
string text = Text ?? string.Empty;
// Determine if any filtering mode is on
bool stringFiltering = TextFilter != null;
bool objectFiltering = FilterMode == AutoCompleteFilterMode.Custom && TextFilter == null;
List<object> items = _items;
// Determine if any filtering mode is on
bool stringFiltering = TextFilter != null;
bool objectFiltering = FilterMode == AutoCompleteFilterMode.Custom && TextFilter == null;
// cache properties
var textFilter = TextFilter;
var itemFilter = ItemFilter;
var _newViewItems = new Collection<object>();
// if the mode is objectFiltering and itemFilter is null, we throw an exception
if (objectFiltering && itemFilter is null)
{
// indicate that filtering is not ongoing anymore
_filterInAction = false;
_cancelRequested = false;
throw new Exception(
"ItemFilter property can not be null when FilterMode has value AutoCompleteFilterMode.Custom");
}
List<object> items = _items;
foreach (object item in items)
{
// Exit the fitter when requested if cancellation is requested
if (_cancelRequested)
// cache properties
var textFilter = TextFilter;
var itemFilter = ItemFilter;
var _newViewItems = new Collection<object>();
// if the mode is objectFiltering and itemFilter is null, we throw an exception
if (objectFiltering && itemFilter is null)
{
return;
throw new Exception(
"ItemFilter property can not be null when FilterMode has value AutoCompleteFilterMode.Custom");
}
bool inResults = !(stringFiltering || objectFiltering);
if (!inResults)
foreach (object item in items)
{
if (stringFiltering)
// Exit the fitter when requested if cancellation is requested
if (_cancelRequested)
{
inResults = textFilter!(text, FormatValue(item));
return;
}
else if (objectFiltering)
bool inResults = !(stringFiltering || objectFiltering);
if (!inResults)
{
inResults = itemFilter!(text, item);
if (stringFiltering)
{
inResults = textFilter!(text, FormatValue(item));
}
else if (objectFiltering)
{
inResults = itemFilter!(text, item);
}
}
}
if (inResults)
{
_newViewItems.Add(item);
if (inResults)
{
_newViewItems.Add(item);
}
}
}
_view?.Clear();
_view?.AddRange(_newViewItems);
// Clear the evaluator to discard a reference to the last item
_valueBindingEvaluator?.ClearDataContext();
_view?.Clear();
_view?.AddRange(_newViewItems);
// indicate that filtering is not ongoing anymore
_filterInAction = false;
_cancelRequested = false;
// Clear the evaluator to discard a reference to the last item
_valueBindingEvaluator?.ClearDataContext();
}
finally
{
// indicate that filtering is not ongoing anymore
_filterInAction = false;
_cancelRequested = false;
}
}
/// <summary>

25
tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs

@ -495,6 +495,31 @@ namespace Avalonia.Controls.UnitTests
});
}
[Fact]
public void Attempting_To_Open_Without_Items_Does_Not_Prevent_Future_Opening_With_Items()
{
RunTest((control, textbox) =>
{
// Allow the drop down to open without anything entered.
control.MinimumPrefixLength = 0;
// Clear the items.
var source = control.ItemsSource;
control.ItemsSource = null;
control.IsDropDownOpen = true;
// DropDown was not actually opened because there are no items.
Assert.False(control.IsDropDownOpen);
// Set the items and try to open the drop down again.
control.ItemsSource = source;
control.IsDropDownOpen = true;
// DropDown can now be opened.
Assert.True(control.IsDropDownOpen);
});
}
/// <summary>
/// Retrieves a defined predicate filter through a new AutoCompleteBox
/// control instance.

Loading…
Cancel
Save