Browse Source

Avoid reset of containers when anchor element is replaced in ItemsRepeater.

* avoid reset of containers when anchor element is replaced

* fix element 0 ownership issue

Ported from https://github.com/microsoft/microsoft-ui-xaml/commit/e45e83d010502911aa1c7e14ea5bf681ceffb048
Steven Kirk 6 years ago
parent
commit
cd04e768f0

+ 32 - 2
src/Avalonia.Layout/ElementManager.cs

@@ -276,8 +276,38 @@ namespace Avalonia.Layout
 
                     case NotifyCollectionChangedAction.Replace:
                     {
-                        OnItemsRemoved(args.OldStartingIndex, args.OldItems.Count);
-                        OnItemsAdded(args.NewStartingIndex, args.NewItems.Count);
+                        int oldSize = args.OldItems.Count;
+                        int newSize = args.NewItems.Count;
+                        int oldStartIndex = args.OldStartingIndex;
+                        int newStartIndex = args.NewStartingIndex;
+
+                        if (oldSize == newSize &&
+                            oldStartIndex == newStartIndex &&
+                            IsDataIndexRealized(oldStartIndex) &&
+                            IsDataIndexRealized(oldStartIndex + oldSize -1))
+                        {
+                            // Straight up replace of n items within the realization window.
+                            // Removing and adding might causes us to lose the anchor causing us
+                            // to throw away all containers and start from scratch.
+                            // Instead, we can just clear those items and set the element to
+                            // null (sentinel) and let the next measure get new containers for them.
+                            var startRealizedIndex = GetRealizedRangeIndexFromDataIndex(oldStartIndex);
+                            for (int realizedIndex = startRealizedIndex; realizedIndex < startRealizedIndex + oldSize; realizedIndex++)
+                            {
+                                var elementRef = _realizedElements[realizedIndex];
+
+                                if (elementRef != null)
+                                {
+                                    _context.RecycleElement(elementRef);
+                                    _realizedElements[realizedIndex] = null;
+                                }
+                            }
+                        }
+                        else
+                        {
+                            OnItemsRemoved(oldStartIndex, oldSize);
+                            OnItemsAdded(newStartIndex, newSize);
+                        }
                     }
                     break;
 

+ 4 - 0
src/Avalonia.Layout/UniformGridLayout.cs

@@ -409,6 +409,10 @@ namespace Avalonia.Layout
                 _orientation.ScrollOrientation,
                 LayoutId);
 
+            // If after Measure the first item is in the realization rect, then we revoke grid state's ownership,
+            // and only use the layout when to clear it when it's done.
+            gridState.EnsureFirstElementOwnership(context);
+
             return new Size(desiredSize.Width, desiredSize.Height);
         }
 

+ 5 - 2
src/Avalonia.Layout/UniformGridLayoutState.cs

@@ -134,10 +134,13 @@ namespace Avalonia.Layout
             }
         }
 
-        internal void EnsureFirstElementOwnership()
+        internal void EnsureFirstElementOwnership(VirtualizingLayoutContext context)
         {
-            if (FlowAlgorithm.GetElementIfRealized(0) != null)
+            if (_cachedFirstElement != null && FlowAlgorithm.GetElementIfRealized(0) != null)
             {
+                // We created the element, but then flowlayout algorithm took ownership, so we can clear it and
+                // let flowlayout algorithm do its thing.
+                context.RecycleElement(_cachedFirstElement);
                 _cachedFirstElement = null;
             }
         }