diff --git a/src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs b/src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs
index 6c761ab4cf..951e60c25b 100644
--- a/src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs
+++ b/src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs
@@ -44,8 +44,8 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly StyledProperty LayoutProperty =
- AvaloniaProperty.Register(nameof(Layout), new StackLayout());
+ public static readonly StyledProperty LayoutProperty =
+ AvaloniaProperty.Register(nameof(Layout), new StackLayout());
///
/// Defines the property.
@@ -53,8 +53,8 @@ namespace Avalonia.Controls
public static readonly StyledProperty VerticalCacheLengthProperty =
AvaloniaProperty.Register(nameof(VerticalCacheLength), 2.0);
- private static readonly StyledProperty VirtualizationInfoProperty =
- AvaloniaProperty.RegisterAttached("VirtualizationInfo");
+ private static readonly StyledProperty VirtualizationInfoProperty =
+ AvaloniaProperty.RegisterAttached("VirtualizationInfo");
internal static readonly Rect InvalidRect = new Rect(-1, -1, -1, -1);
internal static readonly Point ClearedElementsArrangePosition = new Point(-10000.0, -10000.0);
@@ -63,7 +63,7 @@ namespace Avalonia.Controls
private readonly ViewportManager _viewportManager;
private readonly TargetWeakEventSubscriber _layoutWeakSubscriber;
private IEnumerable? _items;
- private VirtualizingLayoutContext? _layoutContext;
+ private RepeaterLayoutContext? _layoutContext;
private EventHandler? _childIndexChanged;
private bool _isLayoutInProgress;
private NotifyCollectionChangedEventArgs? _processingItemsSourceChange;
@@ -104,7 +104,7 @@ namespace Avalonia.Controls
/// The layout used to size and position elements. The default is a StackLayout with
/// vertical orientation.
///
- public AttachedLayout Layout
+ public AttachedLayout? Layout
{
get => GetValue(LayoutProperty);
set => SetValue(LayoutProperty, value);
@@ -164,18 +164,7 @@ namespace Avalonia.Controls
private bool IsProcessingCollectionChange => _processingItemsSourceChange != null;
- private LayoutContext LayoutContext
- {
- get
- {
- if (_layoutContext == null)
- {
- _layoutContext = new RepeaterLayoutContext(this);
- }
-
- return _layoutContext;
- }
- }
+ private RepeaterLayoutContext LayoutContext => _layoutContext ??= new RepeaterLayoutContext(this);
event EventHandler? IChildIndexProvider.ChildIndexChanged
{
@@ -269,39 +258,22 @@ namespace Avalonia.Controls
internal void UnpinElement(Control element) => _viewManager.UpdatePin(element, false);
- internal static VirtualizationInfo? TryGetVirtualizationInfo(Control element)
+ internal static VirtualizationInfo? TryGetVirtualizationInfo(Control? element)
{
- return (element as AvaloniaObject)?.GetValue(VirtualizationInfoProperty);
- }
-
- internal static VirtualizationInfo CreateAndInitializeVirtualizationInfo(Control element)
- {
- if (TryGetVirtualizationInfo(element) != null)
- {
- throw new InvalidOperationException("VirtualizationInfo already created.");
- }
-
- var result = new VirtualizationInfo();
- element.SetValue(VirtualizationInfoProperty, result);
- return result;
+ return element?.GetValue(VirtualizationInfoProperty);
}
internal static VirtualizationInfo GetVirtualizationInfo(Control element)
{
- if (element is AvaloniaObject ao)
- {
- var result = ao.GetValue(VirtualizationInfoProperty);
-
- if (result == null)
- {
- result = new VirtualizationInfo();
- ao.SetValue(VirtualizationInfoProperty, result);
- }
+ var result = element.GetValue(VirtualizationInfoProperty);
- return result;
+ if (result == null)
+ {
+ result = new VirtualizationInfo();
+ element.SetValue(VirtualizationInfoProperty, result);
}
- throw new NotSupportedException("Custom implementations of AvaloniaObject not supported.");
+ return result;
}
private protected override void InvalidateMeasureOnChildrenChanged()
@@ -309,6 +281,7 @@ namespace Avalonia.Controls
// Don't invalidate measure when children change.
}
+ ///
protected override Size MeasureOverride(Size availableSize)
{
if (_isLayoutInProgress)
@@ -334,7 +307,7 @@ namespace Avalonia.Controls
if (layout != null)
{
- var layoutContext = GetLayoutContext();
+ var layoutContext = LayoutContext;
desiredSize = layout.Measure(layoutContext, availableSize);
extent = new Rect(LayoutOrigin.X, LayoutOrigin.Y, desiredSize.Width, desiredSize.Height);
@@ -364,6 +337,7 @@ namespace Avalonia.Controls
}
}
+ ///
protected override Size ArrangeOverride(Size finalSize)
{
if (_isLayoutInProgress)
@@ -380,7 +354,7 @@ namespace Avalonia.Controls
try
{
- var arrangeSize = Layout?.Arrange(GetLayoutContext(), finalSize) ?? default;
+ var arrangeSize = Layout?.Arrange(LayoutContext, finalSize) ?? default;
// The view manager might clear elements during this call.
// That's why we call it before arranging cleared elements
@@ -421,6 +395,7 @@ namespace Avalonia.Controls
}
}
+ ///
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
@@ -428,11 +403,13 @@ namespace Avalonia.Controls
_viewportManager.ResetScrollers();
}
+ ///
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
_viewportManager.ResetScrollers();
}
+ ///
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
if (change.Property == ItemsProperty)
@@ -501,7 +478,7 @@ namespace Avalonia.Controls
if (parent == this)
{
var virtInfo = TryGetVirtualizationInfo(element);
- return _viewManager.GetElementIndex(virtInfo!);
+ return _viewManager.GetElementIndex(virtInfo);
}
return -1;
@@ -529,7 +506,7 @@ namespace Avalonia.Controls
{
if (index >= 0 && index >= (ItemsSourceView?.Count ?? 0))
{
- throw new ArgumentException("Argument index is invalid.", "index");
+ throw new ArgumentException("Argument index is invalid.", nameof(index));
}
if (_isLayoutInProgress)
@@ -547,7 +524,7 @@ namespace Avalonia.Controls
throw new InvalidOperationException("Cannot make an Anchor when there is no attached layout.");
}
- element = (Control)GetLayoutContext().GetOrCreateElementAt(index);
+ element = (Control)LayoutContext.GetOrCreateElementAt(index);
element.Measure(Size.Infinity);
}
@@ -647,9 +624,9 @@ namespace Avalonia.Controls
if (Layout is VirtualizingLayout virtualLayout)
{
- virtualLayout.OnItemsChanged(GetLayoutContext(), newValue, args);
+ virtualLayout.OnItemsChanged(LayoutContext, newValue, args);
}
- else if (Layout is NonVirtualizingLayout nonVirtualLayout)
+ else if (Layout is NonVirtualizingLayout)
{
// Walk through all the elements and make sure they are cleared for
// non-virtualizing layouts.
@@ -693,7 +670,7 @@ namespace Avalonia.Controls
try
{
- virtualLayout.OnItemsChanged(GetLayoutContext(), newValue, args);
+ virtualLayout.OnItemsChanged(LayoutContext, newValue, args);
}
finally
{
@@ -760,7 +737,7 @@ namespace Avalonia.Controls
AttachedLayout.ArrangeInvalidatedWeakEvent.Subscribe(newValue, _layoutWeakSubscriber);
}
- bool isVirtualizingLayout = newValue != null && newValue is VirtualizingLayout;
+ bool isVirtualizingLayout = newValue is VirtualizingLayout;
_viewportManager.OnLayoutChanged(isVirtualizingLayout);
InvalidateMeasure();
}
@@ -788,7 +765,7 @@ namespace Avalonia.Controls
{
if (Layout is VirtualizingLayout virtualLayout)
{
- virtualLayout.OnItemsChanged(GetLayoutContext(), sender, args);
+ virtualLayout.OnItemsChanged(LayoutContext, sender, args);
}
else
{
@@ -807,15 +784,5 @@ namespace Avalonia.Controls
{
_viewportManager.OnBringIntoViewRequested(e);
}
-
- private VirtualizingLayoutContext GetLayoutContext()
- {
- if (_layoutContext == null)
- {
- _layoutContext = new RepeaterLayoutContext(this);
- }
-
- return _layoutContext;
- }
}
}
diff --git a/src/Avalonia.Controls.ItemsRepeater/Controls/ViewManager.cs b/src/Avalonia.Controls.ItemsRepeater/Controls/ViewManager.cs
index 2d302a95dd..6b9d7934bf 100644
--- a/src/Avalonia.Controls.ItemsRepeater/Controls/ViewManager.cs
+++ b/src/Avalonia.Controls.ItemsRepeater/Controls/ViewManager.cs
@@ -53,7 +53,7 @@ namespace Avalonia.Controls
}
}
}
- if (element == null) { element = GetElementFromUniqueIdResetPool(index); };
+ if (element == null) { element = GetElementFromUniqueIdResetPool(index); }
if (element == null) { element = GetElementFromPinnedElements(index); }
if (element == null) { element = GetElementFromElementFactory(index); }
@@ -221,7 +221,7 @@ namespace Avalonia.Controls
return nextElement;
}
- public int GetElementIndex(VirtualizationInfo virtInfo)
+ public int GetElementIndex(VirtualizationInfo? virtInfo)
{
if (virtInfo == null)
{
@@ -627,11 +627,7 @@ namespace Avalonia.Controls
var element = GetElement();
- var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element);
- if (virtInfo == null)
- {
- virtInfo = ItemsRepeater.CreateAndInitializeVirtualizationInfo(element);
- }
+ var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
// Clear flag
virtInfo.MustClearDataContext = false;
@@ -710,9 +706,8 @@ namespace Avalonia.Controls
{
if (parent is ItemsRepeater repeater)
{
- var element = child as Control;
if (repeater == owner &&
- element is not null &&
+ child is Control element &&
ItemsRepeater.GetVirtualizationInfo(element).IsRealized)
{
focusedElement = element;
@@ -722,7 +717,7 @@ namespace Avalonia.Controls
}
child = parent;
- parent = child?.GetVisualParent();
+ parent = child.GetVisualParent();
}
}
diff --git a/src/Avalonia.Controls.ItemsRepeater/Controls/ViewportManager.cs b/src/Avalonia.Controls.ItemsRepeater/Controls/ViewportManager.cs
index 336fb2d228..6ed817c238 100644
--- a/src/Avalonia.Controls.ItemsRepeater/Controls/ViewportManager.cs
+++ b/src/Avalonia.Controls.ItemsRepeater/Controls/ViewportManager.cs
@@ -166,7 +166,7 @@ namespace Avalonia.Controls
if (Math.Abs(_expectedViewportShift.X) > 1 || Math.Abs(_expectedViewportShift.Y) > 1)
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Expecting viewport shift of ({Shift})",
- _owner.Layout.LayoutId, _expectedViewportShift);
+ _owner.Layout?.LayoutId, _expectedViewportShift);
// There are cases where we might be expecting a shift but not get it. We will
// be waiting for the effective viewport event but if the scroll viewer is not able
@@ -287,7 +287,7 @@ namespace Avalonia.Controls
if (_pendingViewportShift.X != 0 || _pendingViewportShift.Y != 0)
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Layout Updated with pending shift {Shift}- invalidating measure",
- _owner.Layout.LayoutId,
+ _owner.Layout?.LayoutId,
_pendingViewportShift);
// Assume this is never going to come.
@@ -436,7 +436,7 @@ namespace Avalonia.Controls
private void OnEffectiveViewportChanged(object? sender, EffectiveViewportChangedEventArgs e)
{
- Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: EffectiveViewportChanged event callback", _owner.Layout.LayoutId);
+ Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: EffectiveViewportChanged event callback", _owner.Layout?.LayoutId);
UpdateViewport(e.EffectiveViewport);
_pendingViewportShift = default;
@@ -490,14 +490,14 @@ namespace Avalonia.Controls
var previousVisibleWindow = _visibleWindow;
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Effective Viewport: ({Before})->({After})",
- _owner.Layout.LayoutId,
+ _owner.Layout?.LayoutId,
previousVisibleWindow,
viewport);
if (-currentVisibleWindow.X <= ItemsRepeater.ClearedElementsArrangePosition.X &&
-currentVisibleWindow.Y <= ItemsRepeater.ClearedElementsArrangePosition.Y)
{
- Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Viewport is invalid. visible window cleared", _owner.Layout.LayoutId);
+ Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Viewport is invalid. visible window cleared", _owner.Layout?.LayoutId);
// We got cleared.
_visibleWindow = default;
}
@@ -509,7 +509,7 @@ namespace Avalonia.Controls
if (_visibleWindow != previousVisibleWindow)
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Used Viewport: ({Before})->({After})",
- _owner.Layout.LayoutId,
+ _owner.Layout?.LayoutId,
previousVisibleWindow,
currentVisibleWindow);
TryInvalidateMeasure();
@@ -532,7 +532,7 @@ namespace Avalonia.Controls
// We invalidate measure instead of just invalidating arrange because
// we don't invalidate measure in UpdateViewport if the view is changing to
// avoid layout cycles.
- Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Invalidating measure due to viewport change", _owner.Layout.LayoutId);
+ Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Invalidating measure due to viewport change", _owner.Layout?.LayoutId);
_owner.InvalidateMeasure();
}
}