Browse Source

add auto-select functionality to ComboBox control

pull/5422/head
luthfiampas 5 years ago
parent
commit
e4cf7a6bf7
  1. 72
      src/Avalonia.Controls/ComboBox.cs
  2. 37
      tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs

72
src/Avalonia.Controls/ComboBox.cs

@ -10,6 +10,7 @@ using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace Avalonia.Controls
@ -76,6 +77,14 @@ namespace Avalonia.Controls
public static readonly StyledProperty<VerticalAlignment> VerticalContentAlignmentProperty =
ContentControl.VerticalContentAlignmentProperty.AddOwner<ComboBox>();
/// <summary>
/// Defines the <see cref="IsAutoSelectEnabled"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsAutoSelectEnabledProperty =
AvaloniaProperty.Register<ComboBox, bool>(nameof(IsAutoSelectEnabled));
private string _autoSelectTerm = string.Empty;
private DispatcherTimer _autoSelectTimer;
private bool _isDropDownOpen;
private Popup _popup;
private object _selectionBoxItem;
@ -164,6 +173,17 @@ namespace Avalonia.Controls
set { SetValue(VerticalContentAlignmentProperty, value); }
}
/// <summary>
/// Gets or sets value indicating whether auto-select is currently enabled.
/// If <c>true</c>, the control will try to find then select matched <see cref="ComboBoxItem"/>
/// based on current keyboard inputs.
/// </summary>
public bool IsAutoSelectEnabled
{
get { return GetValue(IsAutoSelectEnabledProperty); }
set { SetValue(IsAutoSelectEnabledProperty, value); }
}
/// <inheritdoc/>
protected override IItemContainerGenerator CreateItemContainerGenerator()
{
@ -229,6 +249,32 @@ namespace Avalonia.Controls
}
}
/// <inheritdoc />
protected override void OnTextInput(TextInputEventArgs e)
{
if (!IsAutoSelectEnabled || e.Handled)
return;
StopAutoSelectTimer();
_autoSelectTerm += e.Text;
bool match(ItemContainerInfo info) =>
info.ContainerControl is IContentControl control &&
control.Content?.ToString()?.StartsWith(_autoSelectTerm, StringComparison.OrdinalIgnoreCase) == true;
var info = ItemContainerGenerator.Containers.FirstOrDefault(match);
if (info != null)
{
SelectedIndex = info.Index;
}
StartAutoSelectTimer();
e.Handled = true;
}
/// <inheritdoc/>
protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
{
@ -426,5 +472,31 @@ namespace Avalonia.Controls
SelectedIndex = prev;
}
private void StartAutoSelectTimer()
{
_autoSelectTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
_autoSelectTimer.Tick += AutoSelectTimer_Tick;
_autoSelectTimer.Start();
}
private void StopAutoSelectTimer()
{
if (_autoSelectTimer == null)
{
return;
}
_autoSelectTimer.Stop();
_autoSelectTimer.Tick -= AutoSelectTimer_Tick;
_autoSelectTimer = null;
}
private void AutoSelectTimer_Tick(object sender, EventArgs e)
{
_autoSelectTerm = string.Empty;
StopAutoSelectTimer();
}
}
}

37
tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs

@ -1,7 +1,9 @@
using System.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.UnitTests;
@ -137,5 +139,40 @@ namespace Avalonia.Controls.UnitTests
Assert.True(other.IsFocused);
}
}
[Theory]
[InlineData(-1, 2, "c", "A item", "B item", "C item")]
[InlineData(0, 1, "b", "A item", "B item", "C item")]
[InlineData(2, 2, "x", "A item", "B item", "C item")]
public void AutoSelect_Should_Have_Expected_SelectedIndex(
int initialSelectedIndex,
int expectedSelectedIndex,
string searchTerm,
params string[] items)
{
using (UnitTestApplication.Start(TestServices.MockThreadingInterface))
{
var target = new ComboBox
{
IsAutoSelectEnabled = true,
Template = GetTemplate(),
Items = items.Select(x => new ComboBoxItem { Content = x })
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.SelectedIndex = initialSelectedIndex;
var args = new TextInputEventArgs
{
Text = searchTerm,
RoutedEvent = InputElement.TextInputEvent
};
target.RaiseEvent(args);
Assert.Equal(expectedSelectedIndex, target.SelectedIndex);
}
}
}
}

Loading…
Cancel
Save