diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index beec0bdcb16..e20e928f8d8 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections;
-using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
@@ -8,7 +7,6 @@
using Avalonia.Controls.Selection;
using Avalonia.Data;
using Avalonia.Input;
-using Avalonia.Input.Platform;
using Avalonia.Interactivity;
using Avalonia.Metadata;
using Avalonia.Threading;
@@ -187,14 +185,21 @@ public bool AutoScrollToSelectedItem
///
public int SelectedIndex
{
- get =>
+ get
+ {
// When a Begin/EndInit/DataContext update is in place we return the value to be
// updated here, even though it's not yet active and the property changed notification
// has not yet been raised. If we don't do this then the old value will be written back
// to the source when two-way bound, and the update value will be lost.
- _updateState?.SelectedIndex.HasValue == true ?
- _updateState.SelectedIndex.Value :
- Selection.SelectedIndex;
+ if (_updateState is not null)
+ {
+ return _updateState.SelectedIndex.HasValue ?
+ _updateState.SelectedIndex.Value :
+ TryGetExistingSelection()?.SelectedIndex ?? -1;
+ }
+
+ return Selection.SelectedIndex;
+ }
set
{
if (_updateState is object)
@@ -213,11 +218,18 @@ public int SelectedIndex
///
public object? SelectedItem
{
- get =>
- // See SelectedIndex setter for more information.
- _updateState?.SelectedItem.HasValue == true ?
- _updateState.SelectedItem.Value :
- Selection.SelectedItem;
+ get
+ {
+ // See SelectedIndex getter for more information.
+ if (_updateState is not null)
+ {
+ return _updateState.SelectedItem.HasValue ?
+ _updateState.SelectedItem.Value :
+ TryGetExistingSelection()?.SelectedItem;
+ }
+
+ return Selection.SelectedItem;
+ }
set
{
if (_updateState is object)
@@ -270,6 +282,7 @@ protected IList? SelectedItems
{
return _updateState.SelectedItems.Value;
}
+
else if (Selection is InternalSelectionModel ism)
{
var result = ism.WritableSelectedItems;
@@ -456,10 +469,8 @@ private protected override void OnItemsViewCollectionChanged(object? sender, Not
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
- if (Selection?.AnchorIndex is int index)
- {
- AutoScrollToSelectedItemIfNecessary(index);
- }
+
+ AutoScrollToSelectedItemIfNecessary(GetAnchorIndex());
}
///
@@ -470,10 +481,8 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
void ExecuteScrollWhenLayoutUpdated(object? sender, EventArgs e)
{
LayoutUpdated -= ExecuteScrollWhenLayoutUpdated;
- if (Selection?.AnchorIndex is int index)
- {
- AutoScrollToSelectedItemIfNecessary(index);
- }
+
+ AutoScrollToSelectedItemIfNecessary(GetAnchorIndex());
}
if (AutoScrollToSelectedItem)
@@ -482,6 +491,15 @@ void ExecuteScrollWhenLayoutUpdated(object? sender, EventArgs e)
}
}
+ internal int GetAnchorIndex()
+ {
+ var selection = _updateState is not null ? TryGetExistingSelection() : Selection;
+ return selection?.AnchorIndex ?? -1;
+ }
+
+ private ISelectionModel? TryGetExistingSelection()
+ => _updateState?.Selection.HasValue == true ? _updateState.Selection.Value : _selection;
+
protected internal override void PrepareContainerForItemOverride(Control container, object? item, int index)
{
// Ensure that the selection model is created at this point so that accessing it in
@@ -634,10 +652,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
if (change.Property == AutoScrollToSelectedItemProperty)
{
- if (Selection?.AnchorIndex is int index)
- {
- AutoScrollToSelectedItemIfNecessary(index);
- }
+ AutoScrollToSelectedItemIfNecessary(GetAnchorIndex());
}
else if (change.Property == SelectionModeProperty && _selection is object)
{
@@ -671,7 +686,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
return;
}
- var value = change.GetNewValue();
+ var value = change.GetNewValue();
if (value is null)
{
// Clearing SelectedValueBinding makes the SelectedValue the item itself
@@ -921,11 +936,10 @@ private void OnSelectionModelPropertyChanged(object? sender, PropertyChangedEven
if (e.PropertyName == nameof(ISelectionModel.AnchorIndex))
{
_hasScrolledToSelectedItem = false;
- if (Selection?.AnchorIndex is int index)
- {
- KeyboardNavigation.SetTabOnceActiveElement(this, ContainerFromIndex(index));
- AutoScrollToSelectedItemIfNecessary(index);
- }
+
+ var anchorIndex = GetAnchorIndex();
+ KeyboardNavigation.SetTabOnceActiveElement(this, ContainerFromIndex(anchorIndex));
+ AutoScrollToSelectedItemIfNecessary(anchorIndex);
}
else if (e.PropertyName == nameof(ISelectionModel.SelectedIndex) && _oldSelectedIndex != SelectedIndex)
{
@@ -1279,9 +1293,17 @@ private void EndUpdating()
state.SelectedItem = item;
}
+ // SelectedIndex vs SelectedItem:
+ // - If only one has a value, use it
+ // - If both have a value, prefer the one having a "non-empty" value, e.g. not -1 nor null
+ // - If both have a "non-empty" value, prefer the index
if (state.SelectedIndex.HasValue)
{
- SelectedIndex = state.SelectedIndex.Value;
+ var selectedIndex = state.SelectedIndex.Value;
+ if (selectedIndex >= 0 || !state.SelectedItem.HasValue)
+ SelectedIndex = selectedIndex;
+ else
+ SelectedItem = state.SelectedItem.Value;
}
else if (state.SelectedItem.HasValue)
{
@@ -1338,39 +1360,12 @@ private void TextSearchTimer_Tick(object? sender, EventArgs e)
// - Both the old and new SelectionModels have the incorrect Source
private class UpdateState
{
- private Optional _selectedIndex;
- private Optional