diff --git a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
index 491f41ecbf..943fadf100 100644
--- a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
+++ b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
@@ -8,7 +8,6 @@
HorizontalAlignment="Center"
Gap="8">
-
+
+
+
diff --git a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
index 9f181d44f2..6f3b8361cd 100644
--- a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
@@ -6,6 +6,8 @@ using Avalonia.Markup.Xaml.Data;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
namespace ControlCatalog.Pages
{
@@ -109,6 +111,9 @@ namespace ControlCatalog.Pages
var multibindingBox = this.FindControl("MultiBindingBox");
multibindingBox.ValueMemberBinding = binding;
+
+ var asyncBox = this.FindControl("AsyncBox");
+ asyncBox.AsyncPopulator = PopulateAsync;
}
private IEnumerable GetAllAutoCompleteBox()
{
@@ -117,6 +122,19 @@ namespace ControlCatalog.Pages
.OfType();
}
+ private bool StringContains(string str, string query)
+ {
+ return str.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;
+ }
+ private async Task> PopulateAsync(string searchText, CancellationToken cancellationToken)
+ {
+ await Task.Delay(TimeSpan.FromSeconds(1.5), cancellationToken);
+
+ return
+ States.Where(data => StringContains(data.Name, searchText) || StringContains(data.Capital, searchText))
+ .ToList();
+ }
+
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs
index 0e8b93146c..e4962dc069 100644
--- a/src/Avalonia.Controls/AutoCompleteBox.cs
+++ b/src/Avalonia.Controls/AutoCompleteBox.cs
@@ -23,6 +23,8 @@ using Avalonia.Utilities;
using System.Globalization;
using System.Collections.Specialized;
using System.Reactive.Disposables;
+using System.Threading;
+using System.Threading.Tasks;
namespace Avalonia.Controls
{
@@ -360,6 +362,8 @@ namespace Avalonia.Controls
private IDisposable _collectionChangeSubscription;
private IMemberSelector _valueMemberSelector;
+ private Func>> _asyncPopulator;
+ private CancellationTokenSource _populationCancellationTokenSource;
private bool _itemTemplateIsFromValueMemeberBinding = true;
private bool _settingItemTemplateFromValueMemeberBinding;
@@ -559,6 +563,12 @@ namespace Avalonia.Controls
o => o.ValueMemberSelector,
(o, v) => o.ValueMemberSelector = v);
+ public static readonly DirectProperty>>> AsyncPopulatorProperty =
+ AvaloniaProperty.RegisterDirect>>>(
+ nameof(AsyncPopulator),
+ o => o.AsyncPopulator,
+ (o, v) => o.AsyncPopulator = v);
+
private static int ValidateMinimumPrefixLength(AutoCompleteBox control, int value)
{
Contract.Requires(value >= -1);
@@ -1107,6 +1117,12 @@ namespace Avalonia.Controls
set { SetAndRaise(TextFilterProperty, ref _textFilter, value); }
}
+ public Func>> AsyncPopulator
+ {
+ get { return _asyncPopulator; }
+ set { SetAndRaise(AsyncPopulatorProperty, ref _asyncPopulator, value); }
+ }
+
///
/// Gets or sets a collection that is used to generate the items for the
/// drop-down portion of the
@@ -1702,6 +1718,11 @@ namespace Avalonia.Controls
// Update the prefix/search text.
SearchText = Text;
+ if(TryPopulateAsync(SearchText))
+ {
+ return;
+ }
+
// The Populated event enables advanced, custom filtering. The
// client needs to directly update the ItemsSource collection or
// call the Populate method on the control to continue the
@@ -1713,6 +1734,55 @@ namespace Avalonia.Controls
PopulateComplete();
}
}
+ private bool TryPopulateAsync(string searchText)
+ {
+ _populationCancellationTokenSource?.Cancel(false);
+ _populationCancellationTokenSource?.Dispose();
+ _populationCancellationTokenSource = null;
+
+ if(_asyncPopulator == null)
+ {
+ return false;
+ }
+
+ _populationCancellationTokenSource = new CancellationTokenSource();
+ var task = PopulateAsync(searchText, _populationCancellationTokenSource.Token);
+ if (task.Status == TaskStatus.Created)
+ task.Start();
+
+ return true;
+ }
+ private async Task PopulateAsync(string searchText, CancellationToken cancellationToken)
+ {
+
+ try
+ {
+ IEnumerable