committed by
GitHub
21 changed files with 515 additions and 472 deletions
@ -0,0 +1,41 @@ |
|||
<UserControl xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
x:Class="ControlCatalog.Pages.ComboBoxPage"> |
|||
<StackPanel Orientation="Vertical" Spacing="4"> |
|||
<TextBlock Classes="h1">ComboBox</TextBlock> |
|||
<TextBlock Classes="h2">A drop-down list.</TextBlock> |
|||
|
|||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 16 0 0" Spacing="8"> |
|||
<ComboBox SelectedIndex="0"> |
|||
<ComboBoxItem>Inline Items</ComboBoxItem> |
|||
<ComboBoxItem>Inline Item 2</ComboBoxItem> |
|||
<ComboBoxItem>Inline Item 3</ComboBoxItem> |
|||
<ComboBoxItem>Inline Item 4</ComboBoxItem> |
|||
</ComboBox> |
|||
|
|||
<ComboBox SelectedIndex="0"> |
|||
<ComboBoxItem> |
|||
<Panel> |
|||
<Rectangle Fill="{DynamicResource ThemeAccentBrush}"/> |
|||
<TextBlock Margin="8">Control Items</TextBlock> |
|||
</Panel> |
|||
</ComboBoxItem> |
|||
<ComboBoxItem> |
|||
<Ellipse Width="50" Height="50" Fill="Yellow"/> |
|||
</ComboBoxItem> |
|||
<ComboBoxItem> |
|||
<TextBox Text="TextBox"/> |
|||
</ComboBoxItem> |
|||
</ComboBox> |
|||
|
|||
<ComboBox x:Name="fontComboBox" SelectedIndex="0"> |
|||
<ComboBox.ItemTemplate> |
|||
<DataTemplate> |
|||
<TextBlock Text="{Binding Name}" FontFamily="{Binding}" /> |
|||
</DataTemplate> |
|||
</ComboBox.ItemTemplate> |
|||
</ComboBox> |
|||
</StackPanel> |
|||
|
|||
</StackPanel> |
|||
</UserControl> |
|||
@ -1,42 +0,0 @@ |
|||
<UserControl xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
x:Class="ControlCatalog.Pages.DropDownPage"> |
|||
<StackPanel Orientation="Vertical" Spacing="4"> |
|||
<TextBlock Classes="h1">DropDown</TextBlock> |
|||
<TextBlock Classes="h2">A drop-down list.</TextBlock> |
|||
|
|||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 16 0 0" Spacing="8"> |
|||
<DropDown SelectedIndex="0"> |
|||
<DropDownItem>Inline Items</DropDownItem> |
|||
<DropDownItem>Inline Item 2</DropDownItem> |
|||
<DropDownItem>Inline Item 3</DropDownItem> |
|||
<DropDownItem>Inline Item 4</DropDownItem> |
|||
</DropDown> |
|||
|
|||
<DropDown SelectedIndex="0"> |
|||
<DropDownItem> |
|||
<Panel> |
|||
<Rectangle Fill="{DynamicResource ThemeAccentBrush}"/> |
|||
<TextBlock Margin="8">Control Items</TextBlock> |
|||
</Panel> |
|||
</DropDownItem> |
|||
<DropDownItem> |
|||
<Ellipse Width="50" Height="50" Fill="Yellow"/> |
|||
</DropDownItem> |
|||
<DropDownItem> |
|||
<TextBox Text="TextBox"/> |
|||
</DropDownItem> |
|||
</DropDown> |
|||
|
|||
|
|||
<DropDown x:Name="fontDropDown" SelectedIndex="0"> |
|||
<DropDown.ItemTemplate> |
|||
<DataTemplate> |
|||
<TextBlock Text="{Binding Name}" FontFamily="{Binding}" /> |
|||
</DataTemplate> |
|||
</DropDown.ItemTemplate> |
|||
</DropDown> |
|||
</StackPanel> |
|||
|
|||
</StackPanel> |
|||
</UserControl> |
|||
@ -0,0 +1,373 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// 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; |
|||
using Avalonia.VisualTree; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// A drop-down list control.
|
|||
/// </summary>
|
|||
public class ComboBox : SelectingItemsControl |
|||
{ |
|||
/// <summary>
|
|||
/// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
|
|||
/// </summary>
|
|||
private static readonly FuncTemplate<IPanel> DefaultPanel = |
|||
new FuncTemplate<IPanel>(() => new VirtualizingStackPanel()); |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="IsDropDownOpen"/> property.
|
|||
/// </summary>
|
|||
public static readonly DirectProperty<ComboBox, bool> IsDropDownOpenProperty = |
|||
AvaloniaProperty.RegisterDirect<ComboBox, bool>( |
|||
nameof(IsDropDownOpen), |
|||
o => o.IsDropDownOpen, |
|||
(o, v) => o.IsDropDownOpen = v); |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="MaxDropDownHeight"/> property.
|
|||
/// </summary>
|
|||
public static readonly StyledProperty<double> MaxDropDownHeightProperty = |
|||
AvaloniaProperty.Register<ComboBox, double>(nameof(MaxDropDownHeight), 200); |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="SelectionBoxItem"/> property.
|
|||
/// </summary>
|
|||
public static readonly DirectProperty<ComboBox, object> SelectionBoxItemProperty = |
|||
AvaloniaProperty.RegisterDirect<ComboBox, object>(nameof(SelectionBoxItem), o => o.SelectionBoxItem); |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="VirtualizationMode"/> property.
|
|||
/// </summary>
|
|||
public static readonly StyledProperty<ItemVirtualizationMode> VirtualizationModeProperty = |
|||
ItemsPresenter.VirtualizationModeProperty.AddOwner<ComboBox>(); |
|||
|
|||
private bool _isDropDownOpen; |
|||
private Popup _popup; |
|||
private object _selectionBoxItem; |
|||
private IDisposable _subscriptionsOnOpen; |
|||
|
|||
/// <summary>
|
|||
/// Initializes static members of the <see cref="ComboBox"/> class.
|
|||
/// </summary>
|
|||
static ComboBox() |
|||
{ |
|||
ItemsPanelProperty.OverrideDefaultValue<ComboBox>(DefaultPanel); |
|||
FocusableProperty.OverrideDefaultValue<ComboBox>(true); |
|||
SelectedItemProperty.Changed.AddClassHandler<ComboBox>(x => x.SelectedItemChanged); |
|||
KeyDownEvent.AddClassHandler<ComboBox>(x => x.OnKeyDown, Interactivity.RoutingStrategies.Tunnel); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value indicating whether the dropdown is currently open.
|
|||
/// </summary>
|
|||
public bool IsDropDownOpen |
|||
{ |
|||
get { return _isDropDownOpen; } |
|||
set { SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the maximum height for the dropdown list.
|
|||
/// </summary>
|
|||
public double MaxDropDownHeight |
|||
{ |
|||
get { return GetValue(MaxDropDownHeightProperty); } |
|||
set { SetValue(MaxDropDownHeightProperty, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the item to display as the control's content.
|
|||
/// </summary>
|
|||
protected object SelectionBoxItem |
|||
{ |
|||
get { return _selectionBoxItem; } |
|||
set { SetAndRaise(SelectionBoxItemProperty, ref _selectionBoxItem, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the virtualization mode for the items.
|
|||
/// </summary>
|
|||
public ItemVirtualizationMode VirtualizationMode |
|||
{ |
|||
get { return GetValue(VirtualizationModeProperty); } |
|||
set { SetValue(VirtualizationModeProperty, value); } |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override IItemContainerGenerator CreateItemContainerGenerator() |
|||
{ |
|||
return new ItemContainerGenerator<ComboBoxItem>( |
|||
this, |
|||
ComboBoxItem.ContentProperty, |
|||
ComboBoxItem.ContentTemplateProperty); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) |
|||
{ |
|||
base.OnAttachedToLogicalTree(e); |
|||
this.UpdateSelectionBoxItem(this.SelectedItem); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnKeyDown(KeyEventArgs e) |
|||
{ |
|||
base.OnKeyDown(e); |
|||
|
|||
if (e.Handled) |
|||
return; |
|||
|
|||
if (e.Key == Key.F4 || |
|||
((e.Key == Key.Down || e.Key == Key.Up) && ((e.Modifiers & InputModifiers.Alt) != 0))) |
|||
{ |
|||
IsDropDownOpen = !IsDropDownOpen; |
|||
e.Handled = true; |
|||
} |
|||
else if (IsDropDownOpen && e.Key == Key.Escape) |
|||
{ |
|||
IsDropDownOpen = false; |
|||
e.Handled = true; |
|||
} |
|||
else if (IsDropDownOpen && e.Key == Key.Enter) |
|||
{ |
|||
SelectFocusedItem(); |
|||
IsDropDownOpen = false; |
|||
e.Handled = true; |
|||
} |
|||
else if (!IsDropDownOpen) |
|||
{ |
|||
if (e.Key == Key.Down) |
|||
{ |
|||
SelectNext(); |
|||
e.Handled = true; |
|||
} |
|||
else if (e.Key == Key.Up) |
|||
{ |
|||
SelectPrev(); |
|||
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; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnPointerWheelChanged(PointerWheelEventArgs e) |
|||
{ |
|||
base.OnPointerWheelChanged(e); |
|||
|
|||
if (!e.Handled) |
|||
{ |
|||
if (!IsDropDownOpen) |
|||
{ |
|||
if (IsFocused) |
|||
{ |
|||
if (e.Delta.Y < 0) |
|||
SelectNext(); |
|||
else |
|||
SelectPrev(); |
|||
|
|||
e.Handled = true; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
e.Handled = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnPointerPressed(PointerPressedEventArgs e) |
|||
{ |
|||
if (!e.Handled) |
|||
{ |
|||
if (_popup?.PopupRoot != null && ((IVisual)e.Source).GetVisualRoot() == _popup?.PopupRoot) |
|||
{ |
|||
if (UpdateSelectionFromEventSource(e.Source)) |
|||
{ |
|||
_popup?.Close(); |
|||
e.Handled = true; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
IsDropDownOpen = !IsDropDownOpen; |
|||
e.Handled = true; |
|||
} |
|||
} |
|||
|
|||
base.OnPointerPressed(e); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) |
|||
{ |
|||
if (_popup != null) |
|||
{ |
|||
_popup.Opened -= PopupOpened; |
|||
_popup.Closed -= PopupClosed; |
|||
} |
|||
|
|||
_popup = e.NameScope.Get<Popup>("PART_Popup"); |
|||
_popup.Opened += PopupOpened; |
|||
_popup.Closed += PopupClosed; |
|||
|
|||
base.OnTemplateApplied(e); |
|||
} |
|||
|
|||
internal void ItemFocused(ComboBoxItem dropDownItem) |
|||
{ |
|||
if (IsDropDownOpen && dropDownItem.IsFocused && dropDownItem.IsArrangeValid) |
|||
{ |
|||
dropDownItem.BringIntoView(); |
|||
} |
|||
} |
|||
|
|||
private void PopupClosed(object sender, EventArgs e) |
|||
{ |
|||
_subscriptionsOnOpen?.Dispose(); |
|||
_subscriptionsOnOpen = null; |
|||
|
|||
if (CanFocus(this)) |
|||
{ |
|||
Focus(); |
|||
} |
|||
} |
|||
|
|||
private void PopupOpened(object sender, EventArgs e) |
|||
{ |
|||
TryFocusSelectedItem(); |
|||
|
|||
_subscriptionsOnOpen?.Dispose(); |
|||
_subscriptionsOnOpen = null; |
|||
|
|||
var toplevel = this.GetVisualRoot() as TopLevel; |
|||
if (toplevel != null) |
|||
{ |
|||
_subscriptionsOnOpen = toplevel.AddHandler(PointerWheelChangedEvent, (s, ev) => |
|||
{ |
|||
//eat wheel scroll event outside dropdown popup while it's open
|
|||
if (IsDropDownOpen && (ev.Source as IVisual).GetVisualRoot() == toplevel) |
|||
{ |
|||
ev.Handled = true; |
|||
} |
|||
}, Interactivity.RoutingStrategies.Tunnel); |
|||
} |
|||
} |
|||
|
|||
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; |
|||
|
|||
if (contentControl != null) |
|||
{ |
|||
item = contentControl.Content; |
|||
} |
|||
|
|||
var control = item as IControl; |
|||
|
|||
if (control != null) |
|||
{ |
|||
control.Measure(Size.Infinity); |
|||
|
|||
SelectionBoxItem = new Rectangle |
|||
{ |
|||
Width = control.DesiredSize.Width, |
|||
Height = control.DesiredSize.Height, |
|||
Fill = new VisualBrush |
|||
{ |
|||
Visual = control, |
|||
Stretch = Stretch.None, |
|||
AlignmentX = AlignmentX.Left, |
|||
} |
|||
}; |
|||
} |
|||
else |
|||
{ |
|||
var selector = MemberSelector; |
|||
SelectionBoxItem = selector != null ? selector.Select(item) : item; |
|||
} |
|||
} |
|||
|
|||
private void SelectFocusedItem() |
|||
{ |
|||
foreach (ItemContainerInfo dropdownItem in ItemContainerGenerator.Containers) |
|||
{ |
|||
if (dropdownItem.ContainerControl.IsFocused) |
|||
{ |
|||
SelectedIndex = dropdownItem.Index; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void SelectNext() |
|||
{ |
|||
int next = SelectedIndex + 1; |
|||
|
|||
if (next >= ItemCount) |
|||
next = 0; |
|||
|
|||
SelectedIndex = next; |
|||
} |
|||
|
|||
private void SelectPrev() |
|||
{ |
|||
int prev = SelectedIndex - 1; |
|||
|
|||
if (prev < 0) |
|||
prev = ItemCount - 1; |
|||
|
|||
SelectedIndex = prev; |
|||
} |
|||
} |
|||
} |
|||
@ -1,373 +1,28 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// 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; |
|||
using Avalonia.VisualTree; |
|||
using System; |
|||
using Avalonia.Logging; |
|||
using Avalonia.Styling; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// A drop-down list control.
|
|||
/// </summary>
|
|||
public class DropDown : SelectingItemsControl |
|||
[Obsolete("Use ComboBox")] |
|||
public class DropDown : ComboBox, IStyleable |
|||
{ |
|||
/// <summary>
|
|||
/// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
|
|||
/// </summary>
|
|||
private static readonly FuncTemplate<IPanel> DefaultPanel = |
|||
new FuncTemplate<IPanel>(() => new VirtualizingStackPanel()); |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="IsDropDownOpen"/> property.
|
|||
/// </summary>
|
|||
public static readonly DirectProperty<DropDown, bool> IsDropDownOpenProperty = |
|||
AvaloniaProperty.RegisterDirect<DropDown, bool>( |
|||
nameof(IsDropDownOpen), |
|||
o => o.IsDropDownOpen, |
|||
(o, v) => o.IsDropDownOpen = v); |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="MaxDropDownHeight"/> property.
|
|||
/// </summary>
|
|||
public static readonly StyledProperty<double> MaxDropDownHeightProperty = |
|||
AvaloniaProperty.Register<DropDown, double>(nameof(MaxDropDownHeight), 200); |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="SelectionBoxItem"/> property.
|
|||
/// </summary>
|
|||
public static readonly DirectProperty<DropDown, object> SelectionBoxItemProperty = |
|||
AvaloniaProperty.RegisterDirect<DropDown, object>(nameof(SelectionBoxItem), o => o.SelectionBoxItem); |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="VirtualizationMode"/> property.
|
|||
/// </summary>
|
|||
public static readonly StyledProperty<ItemVirtualizationMode> VirtualizationModeProperty = |
|||
ItemsPresenter.VirtualizationModeProperty.AddOwner<DropDown>(); |
|||
|
|||
private bool _isDropDownOpen; |
|||
private Popup _popup; |
|||
private object _selectionBoxItem; |
|||
private IDisposable _subscriptionsOnOpen; |
|||
|
|||
/// <summary>
|
|||
/// Initializes static members of the <see cref="DropDown"/> class.
|
|||
/// </summary>
|
|||
static DropDown() |
|||
{ |
|||
ItemsPanelProperty.OverrideDefaultValue<DropDown>(DefaultPanel); |
|||
FocusableProperty.OverrideDefaultValue<DropDown>(true); |
|||
SelectedItemProperty.Changed.AddClassHandler<DropDown>(x => x.SelectedItemChanged); |
|||
KeyDownEvent.AddClassHandler<DropDown>(x => x.OnKeyDown, Interactivity.RoutingStrategies.Tunnel); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value indicating whether the dropdown is currently open.
|
|||
/// </summary>
|
|||
public bool IsDropDownOpen |
|||
{ |
|||
get { return _isDropDownOpen; } |
|||
set { SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the maximum height for the dropdown list.
|
|||
/// </summary>
|
|||
public double MaxDropDownHeight |
|||
{ |
|||
get { return GetValue(MaxDropDownHeightProperty); } |
|||
set { SetValue(MaxDropDownHeightProperty, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the item to display as the control's content.
|
|||
/// </summary>
|
|||
protected object SelectionBoxItem |
|||
{ |
|||
get { return _selectionBoxItem; } |
|||
set { SetAndRaise(SelectionBoxItemProperty, ref _selectionBoxItem, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the virtualization mode for the items.
|
|||
/// </summary>
|
|||
public ItemVirtualizationMode VirtualizationMode |
|||
{ |
|||
get { return GetValue(VirtualizationModeProperty); } |
|||
set { SetValue(VirtualizationModeProperty, value); } |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override IItemContainerGenerator CreateItemContainerGenerator() |
|||
{ |
|||
return new ItemContainerGenerator<DropDownItem>( |
|||
this, |
|||
DropDownItem.ContentProperty, |
|||
DropDownItem.ContentTemplateProperty); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) |
|||
{ |
|||
base.OnAttachedToLogicalTree(e); |
|||
this.UpdateSelectionBoxItem(this.SelectedItem); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnKeyDown(KeyEventArgs e) |
|||
{ |
|||
base.OnKeyDown(e); |
|||
|
|||
if (e.Handled) |
|||
return; |
|||
|
|||
if (e.Key == Key.F4 || |
|||
((e.Key == Key.Down || e.Key == Key.Up) && ((e.Modifiers & InputModifiers.Alt) != 0))) |
|||
{ |
|||
IsDropDownOpen = !IsDropDownOpen; |
|||
e.Handled = true; |
|||
} |
|||
else if (IsDropDownOpen && e.Key == Key.Escape) |
|||
{ |
|||
IsDropDownOpen = false; |
|||
e.Handled = true; |
|||
} |
|||
else if (IsDropDownOpen && e.Key == Key.Enter) |
|||
{ |
|||
SelectFocusedItem(); |
|||
IsDropDownOpen = false; |
|||
e.Handled = true; |
|||
} |
|||
else if (!IsDropDownOpen) |
|||
{ |
|||
if (e.Key == Key.Down) |
|||
{ |
|||
SelectNext(); |
|||
e.Handled = true; |
|||
} |
|||
else if (e.Key == Key.Up) |
|||
{ |
|||
SelectPrev(); |
|||
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; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnPointerWheelChanged(PointerWheelEventArgs e) |
|||
{ |
|||
base.OnPointerWheelChanged(e); |
|||
|
|||
if (!e.Handled) |
|||
{ |
|||
if (!IsDropDownOpen) |
|||
{ |
|||
if (IsFocused) |
|||
{ |
|||
if (e.Delta.Y < 0) |
|||
SelectNext(); |
|||
else |
|||
SelectPrev(); |
|||
|
|||
e.Handled = true; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
e.Handled = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnPointerPressed(PointerPressedEventArgs e) |
|||
{ |
|||
if (!e.Handled) |
|||
{ |
|||
if (_popup?.PopupRoot != null && ((IVisual)e.Source).GetVisualRoot() == _popup?.PopupRoot) |
|||
{ |
|||
if (UpdateSelectionFromEventSource(e.Source)) |
|||
{ |
|||
_popup?.Close(); |
|||
e.Handled = true; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
IsDropDownOpen = !IsDropDownOpen; |
|||
e.Handled = true; |
|||
} |
|||
} |
|||
|
|||
base.OnPointerPressed(e); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) |
|||
{ |
|||
if (_popup != null) |
|||
{ |
|||
_popup.Opened -= PopupOpened; |
|||
_popup.Closed -= PopupClosed; |
|||
} |
|||
|
|||
_popup = e.NameScope.Get<Popup>("PART_Popup"); |
|||
_popup.Opened += PopupOpened; |
|||
_popup.Closed += PopupClosed; |
|||
|
|||
base.OnTemplateApplied(e); |
|||
} |
|||
|
|||
internal void ItemFocused(DropDownItem dropDownItem) |
|||
public DropDown() |
|||
{ |
|||
if (IsDropDownOpen && dropDownItem.IsFocused && dropDownItem.IsArrangeValid) |
|||
{ |
|||
dropDownItem.BringIntoView(); |
|||
} |
|||
Logger.Warning(LogArea.Control, this, "DropDown is deprecated: Use ComboBox"); |
|||
} |
|||
|
|||
private void PopupClosed(object sender, EventArgs e) |
|||
{ |
|||
_subscriptionsOnOpen?.Dispose(); |
|||
_subscriptionsOnOpen = null; |
|||
|
|||
if (CanFocus(this)) |
|||
{ |
|||
Focus(); |
|||
} |
|||
} |
|||
|
|||
private void PopupOpened(object sender, EventArgs e) |
|||
{ |
|||
TryFocusSelectedItem(); |
|||
|
|||
_subscriptionsOnOpen?.Dispose(); |
|||
_subscriptionsOnOpen = null; |
|||
|
|||
var toplevel = this.GetVisualRoot() as TopLevel; |
|||
if (toplevel != null) |
|||
{ |
|||
_subscriptionsOnOpen = toplevel.AddHandler(PointerWheelChangedEvent, (s, ev) => |
|||
{ |
|||
//eat wheel scroll event outside dropdown popup while it's open
|
|||
if (IsDropDownOpen && (ev.Source as IVisual).GetVisualRoot() == toplevel) |
|||
{ |
|||
ev.Handled = true; |
|||
} |
|||
}, Interactivity.RoutingStrategies.Tunnel); |
|||
} |
|||
} |
|||
|
|||
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; |
|||
|
|||
if (contentControl != null) |
|||
{ |
|||
item = contentControl.Content; |
|||
} |
|||
|
|||
var control = item as IControl; |
|||
|
|||
if (control != null) |
|||
{ |
|||
control.Measure(Size.Infinity); |
|||
|
|||
SelectionBoxItem = new Rectangle |
|||
{ |
|||
Width = control.DesiredSize.Width, |
|||
Height = control.DesiredSize.Height, |
|||
Fill = new VisualBrush |
|||
{ |
|||
Visual = control, |
|||
Stretch = Stretch.None, |
|||
AlignmentX = AlignmentX.Left, |
|||
} |
|||
}; |
|||
} |
|||
else |
|||
{ |
|||
var selector = MemberSelector; |
|||
SelectionBoxItem = selector != null ? selector.Select(item) : item; |
|||
} |
|||
} |
|||
|
|||
private void SelectFocusedItem() |
|||
{ |
|||
foreach (ItemContainerInfo dropdownItem in ItemContainerGenerator.Containers) |
|||
{ |
|||
if (dropdownItem.ContainerControl.IsFocused) |
|||
{ |
|||
SelectedIndex = dropdownItem.Index; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
Type IStyleable.StyleKey => typeof(ComboBox); |
|||
} |
|||
|
|||
private void SelectNext() |
|||
[Obsolete("Use ComboBoxItem")] |
|||
public class DropDownItem : ComboBoxItem, IStyleable |
|||
{ |
|||
public DropDownItem() |
|||
{ |
|||
int next = SelectedIndex + 1; |
|||
|
|||
if (next >= ItemCount) |
|||
next = 0; |
|||
|
|||
SelectedIndex = next; |
|||
Logger.Warning(LogArea.Control, this, "DropDownItem is deprecated: Use ComboBoxItem"); |
|||
} |
|||
|
|||
private void SelectPrev() |
|||
{ |
|||
int prev = SelectedIndex - 1; |
|||
|
|||
if (prev < 0) |
|||
prev = ItemCount - 1; |
|||
|
|||
SelectedIndex = prev; |
|||
} |
|||
Type IStyleable.StyleKey => typeof(ComboBoxItem); |
|||
} |
|||
} |
|||
|
|||
Loading…
Reference in new issue