Browse Source

Merge pull request #11418 from workgroupengineering/fixes/Issue_6263

Fixes Issue #6263
pull/11783/head
Steven Kirk 3 years ago
committed by GitHub
parent
commit
3bb5b2e1ff
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs
  2. 43
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  3. 12
      tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
  4. 10
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

12
samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs

@ -21,7 +21,7 @@ namespace ControlCatalog.ViewModels
public ListBoxPageViewModel() public ListBoxPageViewModel()
{ {
Items = new ObservableCollection<ItemModel>(Enumerable.Range(1, 10000).Select(i => GenerateItem())); Items = new ObservableCollection<ItemModel>(Enumerable.Range(1, 10000).Select(i => GenerateItem()));
Selection = new SelectionModel<ItemModel>(); Selection = new SelectionModel<ItemModel>();
Selection.Select(1); Selection.Select(1);
@ -34,7 +34,13 @@ namespace ControlCatalog.ViewModels
(t ? Avalonia.Controls.SelectionMode.Toggle : 0) | (t ? Avalonia.Controls.SelectionMode.Toggle : 0) |
(a ? Avalonia.Controls.SelectionMode.AlwaysSelected : 0)); (a ? Avalonia.Controls.SelectionMode.AlwaysSelected : 0));
AddItemCommand = MiniCommand.Create(() => Items.Add(GenerateItem())); AddItemCommand = MiniCommand.Create(() =>
{
var item = GenerateItem();
Items.Add(item);
Selection.Clear();
Selection.Select(Items.Count - 1);
});
RemoveItemCommand = MiniCommand.Create(() => RemoveItemCommand = MiniCommand.Create(() =>
{ {
@ -96,7 +102,7 @@ namespace ControlCatalog.ViewModels
public MiniCommand RemoveItemCommand { get; } public MiniCommand RemoveItemCommand { get; }
public MiniCommand SelectRandomItemCommand { get; } public MiniCommand SelectRandomItemCommand { get; }
private ItemModel GenerateItem() => new ItemModel(_counter ++); private ItemModel GenerateItem() => new ItemModel(_counter++);
} }
/// <summary> /// <summary>

43
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -5,9 +5,7 @@ using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Xml.Linq;
using Avalonia.Controls.Selection; using Avalonia.Controls.Selection;
using Avalonia.Controls.Utils;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Platform; using Avalonia.Input.Platform;
@ -171,7 +169,7 @@ namespace Avalonia.Controls.Primitives
/// </summary> /// </summary>
public event EventHandler<SelectionChangedEventArgs>? SelectionChanged public event EventHandler<SelectionChangedEventArgs>? SelectionChanged
{ {
add => AddHandler(SelectionChangedEvent, value); add => AddHandler(SelectionChangedEvent, value);
remove => RemoveHandler(SelectionChangedEvent, value); remove => RemoveHandler(SelectionChangedEvent, value);
} }
@ -369,7 +367,7 @@ namespace Avalonia.Controls.Primitives
/// </summary> /// </summary>
public bool WrapSelection public bool WrapSelection
{ {
get => GetValue(WrapSelectionProperty); get => GetValue(WrapSelectionProperty);
set => SetValue(WrapSelectionProperty, value); set => SetValue(WrapSelectionProperty, value);
} }
@ -382,7 +380,7 @@ namespace Avalonia.Controls.Primitives
/// </remarks> /// </remarks>
protected SelectionMode SelectionMode protected SelectionMode SelectionMode
{ {
get => GetValue(SelectionModeProperty); get => GetValue(SelectionModeProperty);
set => SetValue(SelectionModeProperty, value); set => SetValue(SelectionModeProperty, value);
} }
@ -465,7 +463,10 @@ namespace Avalonia.Controls.Primitives
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{ {
base.OnAttachedToVisualTree(e); base.OnAttachedToVisualTree(e);
AutoScrollToSelectedItemIfNecessary(); if (Selection?.AnchorIndex is int index)
{
AutoScrollToSelectedItemIfNecessary(index);
}
} }
/// <inheritdoc /> /// <inheritdoc />
@ -476,7 +477,10 @@ namespace Avalonia.Controls.Primitives
void ExecuteScrollWhenLayoutUpdated(object? sender, EventArgs e) void ExecuteScrollWhenLayoutUpdated(object? sender, EventArgs e)
{ {
LayoutUpdated -= ExecuteScrollWhenLayoutUpdated; LayoutUpdated -= ExecuteScrollWhenLayoutUpdated;
AutoScrollToSelectedItemIfNecessary(); if (Selection?.AnchorIndex is int index)
{
AutoScrollToSelectedItemIfNecessary(index);
}
} }
if (AutoScrollToSelectedItem) if (AutoScrollToSelectedItem)
@ -625,7 +629,10 @@ namespace Avalonia.Controls.Primitives
if (change.Property == AutoScrollToSelectedItemProperty) if (change.Property == AutoScrollToSelectedItemProperty)
{ {
AutoScrollToSelectedItemIfNecessary(); if (Selection?.AnchorIndex is int index)
{
AutoScrollToSelectedItemIfNecessary(index);
}
} }
else if (change.Property == SelectionModeProperty && _selection is object) else if (change.Property == SelectionModeProperty && _selection is object)
{ {
@ -909,8 +916,11 @@ namespace Avalonia.Controls.Primitives
if (e.PropertyName == nameof(ISelectionModel.AnchorIndex)) if (e.PropertyName == nameof(ISelectionModel.AnchorIndex))
{ {
_hasScrolledToSelectedItem = false; _hasScrolledToSelectedItem = false;
KeyboardNavigation.SetTabOnceActiveElement(this, ContainerFromIndex(Selection.AnchorIndex)); if (Selection?.AnchorIndex is int index)
AutoScrollToSelectedItemIfNecessary(); {
KeyboardNavigation.SetTabOnceActiveElement(this, ContainerFromIndex(index));
AutoScrollToSelectedItemIfNecessary(index);
}
} }
else if (e.PropertyName == nameof(ISelectionModel.SelectedIndex) && _oldSelectedIndex != SelectedIndex) else if (e.PropertyName == nameof(ISelectionModel.SelectedIndex) && _oldSelectedIndex != SelectedIndex)
{ {
@ -1038,7 +1048,7 @@ namespace Avalonia.Controls.Primitives
return value; return value;
} }
else else
{ {
return AvaloniaProperty.UnsetValue; return AvaloniaProperty.UnsetValue;
} }
} }
@ -1096,16 +1106,19 @@ namespace Avalonia.Controls.Primitives
} }
} }
private void AutoScrollToSelectedItemIfNecessary() private void AutoScrollToSelectedItemIfNecessary(int anchorIndex)
{ {
if (AutoScrollToSelectedItem && if (AutoScrollToSelectedItem &&
!_hasScrolledToSelectedItem && !_hasScrolledToSelectedItem &&
Presenter is object && Presenter is object &&
Selection.AnchorIndex >= 0 && anchorIndex >= 0 &&
IsAttachedToVisualTree) IsAttachedToVisualTree)
{ {
ScrollIntoView(Selection.AnchorIndex); Dispatcher.UIThread.Post(state =>
_hasScrolledToSelectedItem = true; {
ScrollIntoView((int)state!);
_hasScrolledToSelectedItem = true;
}, anchorIndex);
} }
} }

12
tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

@ -432,6 +432,8 @@ namespace Avalonia.Controls.UnitTests
items.Remove("1"); items.Remove("1");
lm.ExecuteLayoutPass(); lm.ExecuteLayoutPass();
Threading.Dispatcher.UIThread.RunJobs();
Assert.Equal("30", target.ContainerFromIndex(items.Count - 1).DataContext); Assert.Equal("30", target.ContainerFromIndex(items.Count - 1).DataContext);
Assert.Equal("29", target.ContainerFromIndex(items.Count - 2).DataContext); Assert.Equal("29", target.ContainerFromIndex(items.Count - 2).DataContext);
Assert.Equal("28", target.ContainerFromIndex(items.Count - 3).DataContext); Assert.Equal("28", target.ContainerFromIndex(items.Count - 3).DataContext);
@ -457,8 +459,13 @@ namespace Avalonia.Controls.UnitTests
Prepare(target); Prepare(target);
Threading.Dispatcher.UIThread.RunJobs();
// First an item that is not index 0 must be selected. // First an item that is not index 0 must be selected.
_mouse.Click(target.Presenter.Panel.Children[1]); _mouse.Click(target.Presenter.Panel.Children[1]);
Threading.Dispatcher.UIThread.RunJobs();
Assert.Equal(1, target.Selection.AnchorIndex); Assert.Equal(1, target.Selection.AnchorIndex);
// We're going to be clicking on item 9. // We're going to be clicking on item 9.
@ -471,6 +478,7 @@ namespace Avalonia.Controls.UnitTests
// into view due to SelectionMode.AlwaysSelected. // into view due to SelectionMode.AlwaysSelected.
target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) =>
{ {
Assert.Same(item, e.TargetObject); Assert.Same(item, e.TargetObject);
++raised; ++raised;
}); });
@ -478,6 +486,8 @@ namespace Avalonia.Controls.UnitTests
// Click item 9. // Click item 9.
_mouse.Click(item); _mouse.Click(item);
Threading.Dispatcher.UIThread.RunJobs();
Assert.Equal(1, raised); Assert.Equal(1, raised);
} }
} }
@ -744,6 +754,8 @@ namespace Avalonia.Controls.UnitTests
items.Reverse(); items.Reverse();
Layout(target); Layout(target);
Threading.Dispatcher.UIThread.RunJobs();
realized = target.GetRealizedContainers() realized = target.GetRealizedContainers()
.Cast<ListBoxItem>() .Cast<ListBoxItem>()
.Select(x => (string)x.DataContext) .Select(x => (string)x.DataContext)

10
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@ -1536,7 +1536,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
Prepare(target); Prepare(target);
target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => raised = true); target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => raised = true);
target.SelectedIndex = 2; target.SelectedIndex = 2;
Threading.Dispatcher.UIThread.RunJobs();
Assert.True(raised); Assert.True(raised);
} }
@ -1561,7 +1561,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => raised = true); target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => raised = true);
target.SelectedIndex = 2; target.SelectedIndex = 2;
Prepare(target); Prepare(target);
Threading.Dispatcher.UIThread.RunJobs();
Assert.True(raised); Assert.True(raised);
} }
@ -1632,7 +1632,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
root.Child = null; root.Child = null;
target.SelectedIndex = 1; target.SelectedIndex = 1;
root.Child = target; root.Child = target;
Threading.Dispatcher.UIThread.RunJobs();
Assert.True(raised); Assert.True(raised);
} }
@ -1689,11 +1689,11 @@ namespace Avalonia.Controls.UnitTests.Primitives
var raised = false; var raised = false;
target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => raised = true); target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => raised = true);
target.SelectedIndex = 2; target.SelectedIndex = 2;
Threading.Dispatcher.UIThread.RunJobs();
Assert.False(raised); Assert.False(raised);
target.AutoScrollToSelectedItem = true; target.AutoScrollToSelectedItem = true;
Threading.Dispatcher.UIThread.RunJobs();
Assert.True(raised); Assert.True(raised);
} }

Loading…
Cancel
Save