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()
{
Items = new ObservableCollection<ItemModel>(Enumerable.Range(1, 10000).Select(i => GenerateItem()));
Selection = new SelectionModel<ItemModel>();
Selection.Select(1);
@ -34,7 +34,13 @@ namespace ControlCatalog.ViewModels
(t ? Avalonia.Controls.SelectionMode.Toggle : 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(() =>
{
@ -96,7 +102,7 @@ namespace ControlCatalog.ViewModels
public MiniCommand RemoveItemCommand { get; }
public MiniCommand SelectRandomItemCommand { get; }
private ItemModel GenerateItem() => new ItemModel(_counter ++);
private ItemModel GenerateItem() => new ItemModel(_counter++);
}
/// <summary>

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

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

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

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

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

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

Loading…
Cancel
Save