diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs
index e2d2f0a516..7fdb75a9fc 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/src/Avalonia.Controls/ItemsControl.cs
@@ -330,31 +330,34 @@ namespace Avalonia.Controls
/// The key events.
protected override void OnKeyDown(KeyEventArgs e)
{
- var focus = FocusManager.Instance;
- var direction = e.Key.ToNavigationDirection();
- var container = Presenter?.Panel as INavigableContainer;
-
- if (container == null ||
- focus.Current == null ||
- direction == null ||
- direction.Value.IsTab())
+ if (!e.Handled)
{
- return;
- }
-
- var current = focus.Current
- .GetSelfAndVisualAncestors()
- .OfType()
- .FirstOrDefault(x => x.VisualParent == container);
+ var focus = FocusManager.Instance;
+ var direction = e.Key.ToNavigationDirection();
+ var container = Presenter?.Panel as INavigableContainer;
+
+ if (container == null ||
+ focus.Current == null ||
+ direction == null ||
+ direction.Value.IsTab())
+ {
+ return;
+ }
- if (current != null)
- {
- var next = container.GetControl(direction.Value, current);
+ var current = focus.Current
+ .GetSelfAndVisualAncestors()
+ .OfType()
+ .FirstOrDefault(x => x.VisualParent == container);
- if (next != null)
+ if (current != null)
{
- focus.Focus(next, NavigationMethod.Directional);
- e.Handled = true;
+ var next = GetNextControl(container, direction.Value, current);
+
+ if (next != null)
+ {
+ focus.Focus(next, NavigationMethod.Directional);
+ e.Handled = true;
+ }
}
}
@@ -498,5 +501,27 @@ namespace Avalonia.Controls
ItemCount = Items.Count();
}
}
+
+ protected static IInputElement GetNextControl(
+ INavigableContainer container,
+ NavigationDirection direction,
+ IInputElement from)
+ {
+ IInputElement result;
+
+ while (from != null)
+ {
+ result = container.GetControl(direction, from);
+
+ if (result?.Focusable == true)
+ {
+ return result;
+ }
+
+ from = result;
+ }
+
+ return null;
+ }
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
index 9ef1e9f0d2..3cf886ade4 100644
--- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
@@ -11,6 +11,7 @@ using Avalonia.VisualTree;
using Xunit;
using System.Collections.ObjectModel;
using Avalonia.UnitTests;
+using Avalonia.Input;
namespace Avalonia.Controls.UnitTests
{
@@ -494,6 +495,77 @@ namespace Avalonia.Controls.UnitTests
Assert.NotNull(NameScope.GetNameScope((TextBlock)container.Child));
}
+ [Fact]
+ public void Focuses_Next_Item_On_Key_Down()
+ {
+ using (UnitTestApplication.Start(TestServices.RealFocus))
+ {
+ var items = new object[]
+ {
+ new Button(),
+ new Button(),
+ };
+
+ var target = new ItemsControl
+ {
+ Template = GetTemplate(),
+ Items = items,
+ };
+
+ var root = new TestRoot { Child = target };
+
+ target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
+ target.Presenter.Panel.Children[0].Focus();
+
+ target.RaiseEvent(new KeyEventArgs
+ {
+ RoutedEvent = InputElement.KeyDownEvent,
+ Key = Key.Down,
+ });
+
+ Assert.Equal(
+ target.Presenter.Panel.Children[1],
+ FocusManager.Instance.Current);
+ }
+ }
+
+ [Fact]
+ public void Does_Not_Focus_Non_Focusable_Item_On_Key_Down()
+ {
+ using (UnitTestApplication.Start(TestServices.RealFocus))
+ {
+ var items = new object[]
+ {
+ new Button(),
+ new Button { Focusable = false },
+ new Button(),
+ };
+
+ var target = new ItemsControl
+ {
+ Template = GetTemplate(),
+ Items = items,
+ };
+
+ var root = new TestRoot { Child = target };
+
+ target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
+ target.Presenter.Panel.Children[0].Focus();
+
+ target.RaiseEvent(new KeyEventArgs
+ {
+ RoutedEvent = InputElement.KeyDownEvent,
+ Key = Key.Down,
+ });
+
+ Assert.Equal(
+ target.Presenter.Panel.Children[2],
+ FocusManager.Instance.Current);
+ }
+ }
+
private class Item
{
public Item(string value)