diff --git a/src/Avalonia.Controls/DropDown.cs b/src/Avalonia.Controls/DropDown.cs
index 8596d06d2c..93b33e0589 100644
--- a/src/Avalonia.Controls/DropDown.cs
+++ b/src/Avalonia.Controls/DropDown.cs
@@ -2,9 +2,12 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
+using System.Linq;
using Avalonia.Controls.Generators;
+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;
@@ -12,12 +15,17 @@ using Avalonia.VisualTree;
namespace Avalonia.Controls
{
-
///
/// A drop-down list control.
///
public class DropDown : SelectingItemsControl
{
+ ///
+ /// The default value for the property.
+ ///
+ private static readonly FuncTemplate DefaultPanel =
+ new FuncTemplate(() => new VirtualizingStackPanel());
+
///
/// Defines the property.
///
@@ -39,6 +47,12 @@ namespace Avalonia.Controls
public static readonly DirectProperty SelectionBoxItemProperty =
AvaloniaProperty.RegisterDirect(nameof(SelectionBoxItem), o => o.SelectionBoxItem);
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty VirtualizationModeProperty =
+ ItemsPresenter.VirtualizationModeProperty.AddOwner();
+
private bool _isDropDownOpen;
private Popup _popup;
private object _selectionBoxItem;
@@ -48,6 +62,7 @@ namespace Avalonia.Controls
///
static DropDown()
{
+ ItemsPanelProperty.OverrideDefaultValue(DefaultPanel);
FocusableProperty.OverrideDefaultValue(true);
SelectedItemProperty.Changed.AddClassHandler(x => x.SelectedItemChanged);
KeyDownEvent.AddClassHandler(x => x.OnKeyDown, Interactivity.RoutingStrategies.Tunnel);
@@ -80,6 +95,15 @@ namespace Avalonia.Controls
set { SetAndRaise(SelectionBoxItemProperty, ref _selectionBoxItem, value); }
}
+ ///
+ /// Gets or sets the virtualization mode for the items.
+ ///
+ public ItemVirtualizationMode VirtualizationMode
+ {
+ get { return GetValue(VirtualizationModeProperty); }
+ set { SetValue(VirtualizationModeProperty, value); }
+ }
+
///
protected override IItemContainerGenerator CreateItemContainerGenerator()
{
@@ -138,6 +162,16 @@ namespace Avalonia.Controls
e.Handled = true;
}
}
+ else if (IsDropDownOpen && SelectedIndex < 0 && ItemCount > 0 &&
+ (e.Key == Key.Up || e.Key == Key.Down))
+ {
+ var firstChild = Presenter?.Panel?.Children.FirstOrDefault(c => CanFocus(c));
+ if (firstChild != null)
+ {
+ FocusManager.Instance?.Focus(firstChild, NavigationMethod.Directional);
+ e.Handled = true;
+ }
+ }
}
///
@@ -159,6 +193,7 @@ namespace Avalonia.Controls
e.Handled = true;
}
}
+
base.OnPointerPressed(e);
}
@@ -168,28 +203,65 @@ namespace Avalonia.Controls
if (_popup != null)
{
_popup.Opened -= PopupOpened;
+ _popup.Closed -= PopupClosed;
}
_popup = e.NameScope.Get("PART_Popup");
_popup.Opened += PopupOpened;
+ _popup.Closed += PopupClosed;
+
+ base.OnTemplateApplied(e);
}
- private void PopupOpened(object sender, EventArgs e)
+ internal void ItemFocused(DropDownItem dropDownItem)
{
- var selectedIndex = SelectedIndex;
+ if (IsDropDownOpen && dropDownItem.IsFocused && dropDownItem.IsArrangeValid)
+ {
+ dropDownItem.BringIntoView();
+ }
+ }
- if (selectedIndex != -1)
+ private void PopupClosed(object sender, EventArgs e)
+ {
+ if (CanFocus(this))
{
- var container = ItemContainerGenerator.ContainerFromIndex(selectedIndex);
- container?.Focus();
+ Focus();
}
}
+ private void PopupOpened(object sender, EventArgs e)
+ {
+ TryFocusSelectedItem();
+ }
+
private void SelectedItemChanged(AvaloniaPropertyChangedEventArgs e)
{
UpdateSelectionBoxItem(e.NewValue);
+ TryFocusSelectedItem();
+ }
+
+ private void TryFocusSelectedItem()
+ {
+ var selectedIndex = SelectedIndex;
+ if (IsDropDownOpen && selectedIndex != -1)
+ {
+ var container = ItemContainerGenerator.ContainerFromIndex(selectedIndex);
+
+ if(container == null && SelectedItems.Count > 0)
+ {
+ ScrollIntoView(SelectedItems[0]);
+ container = ItemContainerGenerator.ContainerFromIndex(selectedIndex);
+ }
+
+ if (container != null && CanFocus(container))
+ {
+ container.Focus();
+ }
+ }
}
+ private bool CanFocus(IControl control) => control.Focusable && control.IsEnabledCore && control.IsVisible;
+
private void UpdateSelectionBoxItem(object item)
{
var contentControl = item as IContentControl;
@@ -219,7 +291,8 @@ namespace Avalonia.Controls
}
else
{
- SelectionBoxItem = item;
+ var selector = MemberSelector;
+ SelectionBoxItem = selector != null ? selector.Select(item) : item;
}
}
diff --git a/src/Avalonia.Controls/DropDownItem.cs b/src/Avalonia.Controls/DropDownItem.cs
index fb465e93ec..1e22ededf6 100644
--- a/src/Avalonia.Controls/DropDownItem.cs
+++ b/src/Avalonia.Controls/DropDownItem.cs
@@ -2,43 +2,19 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
+using System.Reactive.Linq;
namespace Avalonia.Controls
{
///
/// A selectable item in a .
///
- public class DropDownItem : ContentControl, ISelectable
+ public class DropDownItem : ListBoxItem
{
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty IsSelectedProperty =
- AvaloniaProperty.Register(nameof(IsSelected));
-
- ///
- /// Initializes static members of the class.
- ///
- static DropDownItem()
- {
- FocusableProperty.OverrideDefaultValue(true);
- }
-
public DropDownItem()
{
- this.GetObservable(DropDownItem.IsFocusedProperty).Subscribe(focused =>
- {
- PseudoClasses.Set(":selected", focused);
- });
- }
-
- ///
- /// Gets or sets the selection state of the item.
- ///
- public bool IsSelected
- {
- get { return GetValue(IsSelectedProperty); }
- set { SetValue(IsSelectedProperty, value); }
+ this.GetObservable(DropDownItem.IsFocusedProperty).Where(focused => focused)
+ .Subscribe(_ => (Parent as DropDown)?.ItemFocused(this));
}
}
}
diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index ccbdc71b1d..c40ddc37ad 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
@@ -431,9 +431,12 @@ namespace Avalonia.Controls.Primitives
{
if (i.ContainerControl != null && i.Item != null)
{
- MarkContainerSelected(
- i.ContainerControl,
- SelectedItems.Contains(i.Item));
+ var ms = MemberSelector;
+ bool selected = ms == null ?
+ SelectedItems.Contains(i.Item) :
+ SelectedItems.OfType
@@ -54,4 +60,4 @@
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Default/DropDownItem.xaml b/src/Avalonia.Themes.Default/DropDownItem.xaml
index f52608c0a8..f542a34d71 100644
--- a/src/Avalonia.Themes.Default/DropDownItem.xaml
+++ b/src/Avalonia.Themes.Default/DropDownItem.xaml
@@ -18,7 +18,7 @@
-
+
diff --git a/src/Avalonia.Themes.Default/FocusAdorner.xaml b/src/Avalonia.Themes.Default/FocusAdorner.xaml
index 573c43dc8d..2d5e369573 100644
--- a/src/Avalonia.Themes.Default/FocusAdorner.xaml
+++ b/src/Avalonia.Themes.Default/FocusAdorner.xaml
@@ -3,7 +3,8 @@
+ StrokeDashArray="1,2"
+ Margin="1"/>
-
\ No newline at end of file
+