Browse Source

Added async population feature

pull/1427/head
sdoroff 8 years ago
parent
commit
242b9251df
  1. 8
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
  2. 18
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
  3. 70
      src/Avalonia.Controls/AutoCompleteBox.cs

8
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml

@ -8,7 +8,6 @@
HorizontalAlignment="Center"
Gap="8">
<StackPanel Orientation="Vertical">
<TextBlock Text="MinimumPrefixLength: 1"/>
<AutoCompleteBox Width="200"
Margin="0,0,0,8"
@ -35,6 +34,7 @@
<StackPanel Orientation="Vertical">
<TextBlock Text="ValueMemeberSelector"/>
<AutoCompleteBox Width="200"
Margin="0,0,0,8"
@ -47,8 +47,12 @@
<AutoCompleteBox Name="MultiBindingBox"
Width="200"
Margin="0,0,0,8"
ValueMemberBinding="{Binding Capital}"
FilterMode="Contains"/>
<TextBlock Text="Async Populate"/>
<AutoCompleteBox Name="AsyncBox"
Width="200"
Margin="0,0,0,8"
FilterMode="None"/>
</StackPanel>
</StackPanel>
</StackPanel>

18
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<AutoCompleteBox>("MultiBindingBox");
multibindingBox.ValueMemberBinding = binding;
var asyncBox = this.FindControl<AutoCompleteBox>("AsyncBox");
asyncBox.AsyncPopulator = PopulateAsync;
}
private IEnumerable<AutoCompleteBox> GetAllAutoCompleteBox()
{
@ -117,6 +122,19 @@ namespace ControlCatalog.Pages
.OfType<AutoCompleteBox>();
}
private bool StringContains(string str, string query)
{
return str.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;
}
private async Task<IEnumerable<object>> 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);

70
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<string, CancellationToken, Task<IEnumerable<object>>> _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<AutoCompleteBox, Func<string, CancellationToken, Task<IEnumerable<object>>>> AsyncPopulatorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, Func<string, CancellationToken, Task<IEnumerable<object>>>>(
nameof(AsyncPopulator),
o => o.AsyncPopulator,
(o, v) => o.AsyncPopulator = v);
private static int ValidateMinimumPrefixLength(AutoCompleteBox control, int value)
{
Contract.Requires<ArgumentOutOfRangeException>(value >= -1);
@ -1107,6 +1117,12 @@ namespace Avalonia.Controls
set { SetAndRaise(TextFilterProperty, ref _textFilter, value); }
}
public Func<string, CancellationToken, Task<IEnumerable<object>>> AsyncPopulator
{
get { return _asyncPopulator; }
set { SetAndRaise(AsyncPopulatorProperty, ref _asyncPopulator, value); }
}
/// <summary>
/// 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<object> result = await _asyncPopulator.Invoke(searchText, cancellationToken);
var resultList = result.ToList();
if (cancellationToken.IsCancellationRequested)
{
return;
}
await Dispatcher.UIThread.InvokeAsync(() =>
{
if (!cancellationToken.IsCancellationRequested)
{
Items = resultList;
PopulateComplete();
}
});
}
catch (TaskCanceledException)
{ }
finally
{
_populationCancellationTokenSource?.Dispose();
_populationCancellationTokenSource = null;
}
}
/// <summary>
/// Private method that directly opens the popup, checks the expander

Loading…
Cancel
Save