diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs index 9ed09df7db..fb2da09e73 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs @@ -369,6 +369,12 @@ namespace Avalonia.Controls { var newBounds = element.Bounds; virtInfo.ArrangeBounds = newBounds; + + if (!virtInfo.IsRegisteredAsAnchorCandidate) + { + _viewportManager.RegisterScrollAnchorCandidate(element); + virtInfo.IsRegisteredAsAnchorCandidate = true; + } } } @@ -520,11 +526,14 @@ namespace Avalonia.Controls return element; } - internal void OnElementPrepared(IControl element, int index) + internal void OnElementPrepared(IControl element, VirtualizationInfo virtInfo) { - _viewportManager.OnElementPrepared(element); + _viewportManager.OnElementPrepared(element, virtInfo); + if (ElementPrepared != null) { + var index = virtInfo.Index; + if (_elementPreparedArgs == null) { _elementPreparedArgs = new ItemsRepeaterElementPreparedEventArgs(element, index); diff --git a/src/Avalonia.Controls/Repeater/ViewManager.cs b/src/Avalonia.Controls/Repeater/ViewManager.cs index 416b1e2824..cf2066b373 100644 --- a/src/Avalonia.Controls/Repeater/ViewManager.cs +++ b/src/Avalonia.Controls/Repeater/ViewManager.cs @@ -661,7 +661,7 @@ namespace Avalonia.Controls children.Add(element); } - repeater.OnElementPrepared(element, index); + repeater.OnElementPrepared(element, virtInfo); // Update realized indices _firstRealizedElementIndexHeldByLayout = Math.Min(_firstRealizedElementIndexHeldByLayout, index); diff --git a/src/Avalonia.Controls/Repeater/ViewportManager.cs b/src/Avalonia.Controls/Repeater/ViewportManager.cs index bdb0fa3270..6e24408aa9 100644 --- a/src/Avalonia.Controls/Repeater/ViewportManager.cs +++ b/src/Avalonia.Controls/Repeater/ViewportManager.cs @@ -240,9 +240,14 @@ namespace Avalonia.Controls } } - public void OnElementPrepared(IControl element) + public void OnElementPrepared(IControl element, VirtualizationInfo virtInfo) { - _scroller?.RegisterAnchorCandidate(element); + // WinUI registers the element as an anchor candidate here, but I feel that's in error: + // at this point the element has not yet been positioned by the arrange pass so it will + // have its previous position, meaning that when the arrange pass moves it into its new + // position, an incorrect scroll anchoring will occur. Instead signal that it's not yet + // registered as a scroll anchor candidate. + virtInfo.IsRegisteredAsAnchorCandidate = false; } public void OnElementCleared(IControl element) @@ -373,6 +378,11 @@ namespace Avalonia.Controls } } + public void RegisterScrollAnchorCandidate(IControl element) + { + _scroller?.RegisterAnchorCandidate(element); + } + private IControl GetImmediateChildOfRepeater(IControl descendant) { var targetChild = descendant; diff --git a/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs b/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs index 7a639419c1..f8cfde609e 100644 --- a/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs +++ b/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs @@ -38,6 +38,7 @@ namespace Avalonia.Controls public bool IsInUniqueIdResetPool => Owner == ElementOwner.UniqueIdResetPool; public bool MustClearDataContext { get; set; } public bool KeepAlive { get; set; } + public bool IsRegisteredAsAnchorCandidate { get; set; } public ElementOwner Owner { get; private set; } = ElementOwner.ElementFactory; public string UniqueId { get; private set; }