|
|
|
@ -3,8 +3,11 @@ using System.Collections.Generic; |
|
|
|
using System.Collections.Specialized; |
|
|
|
using System.Diagnostics; |
|
|
|
using System.Linq; |
|
|
|
using System.Reflection; |
|
|
|
using Avalonia.Controls.Primitives; |
|
|
|
using Avalonia.Controls.Utils; |
|
|
|
using Avalonia.Input; |
|
|
|
using Avalonia.Interactivity; |
|
|
|
using Avalonia.Layout; |
|
|
|
using Avalonia.Utilities; |
|
|
|
using Avalonia.VisualTree; |
|
|
|
@ -14,7 +17,7 @@ namespace Avalonia.Controls |
|
|
|
/// <summary>
|
|
|
|
/// Arranges and virtualizes content on a single line that is oriented either horizontally or vertically.
|
|
|
|
/// </summary>
|
|
|
|
public class VirtualizingStackPanel : VirtualizingPanel |
|
|
|
public class VirtualizingStackPanel : VirtualizingPanel, IScrollSnapPointsInfo |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// Defines the <see cref="Orientation"/> property.
|
|
|
|
@ -22,6 +25,34 @@ namespace Avalonia.Controls |
|
|
|
public static readonly StyledProperty<Orientation> OrientationProperty = |
|
|
|
StackLayout.OrientationProperty.AddOwner<VirtualizingStackPanel>(); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Defines the <see cref="AreHorizontalSnapPointsRegular"/> property.
|
|
|
|
/// </summary>
|
|
|
|
public static readonly StyledProperty<bool> AreHorizontalSnapPointsRegularProperty = |
|
|
|
AvaloniaProperty.Register<VirtualizingStackPanel, bool>(nameof(AreHorizontalSnapPointsRegular)); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Defines the <see cref="AreVerticalSnapPointsRegular"/> property.
|
|
|
|
/// </summary>
|
|
|
|
public static readonly StyledProperty<bool> AreVerticalSnapPointsRegularProperty = |
|
|
|
AvaloniaProperty.Register<StackPanel, bool>(nameof(AreVerticalSnapPointsRegular)); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Defines the <see cref="HorizontalSnapPointsChanged"/> event.
|
|
|
|
/// </summary>
|
|
|
|
public static readonly RoutedEvent<RoutedEventArgs> HorizontalSnapPointsChangedEvent = |
|
|
|
RoutedEvent.Register<VirtualizingStackPanel, RoutedEventArgs>( |
|
|
|
nameof(HorizontalSnapPointsChanged), |
|
|
|
RoutingStrategies.Bubble); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Defines the <see cref="VerticalSnapPointsChanged"/> event.
|
|
|
|
/// </summary>
|
|
|
|
public static readonly RoutedEvent<RoutedEventArgs> VerticalSnapPointsChangedEvent = |
|
|
|
RoutedEvent.Register<VirtualizingStackPanel, RoutedEventArgs>( |
|
|
|
nameof(VerticalSnapPointsChanged), |
|
|
|
RoutingStrategies.Bubble); |
|
|
|
|
|
|
|
private static readonly AttachedProperty<bool> ItemIsOwnContainerProperty = |
|
|
|
AvaloniaProperty.RegisterAttached<VirtualizingStackPanel, Control, bool>("ItemIsOwnContainer"); |
|
|
|
|
|
|
|
@ -62,6 +93,42 @@ namespace Avalonia.Controls |
|
|
|
set => SetValue(OrientationProperty, value); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Occurs when the measurements for horizontal snap points change.
|
|
|
|
/// </summary>
|
|
|
|
public event EventHandler<RoutedEventArgs>? HorizontalSnapPointsChanged |
|
|
|
{ |
|
|
|
add => AddHandler(HorizontalSnapPointsChangedEvent, value); |
|
|
|
remove => RemoveHandler(HorizontalSnapPointsChangedEvent, value); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Occurs when the measurements for vertical snap points change.
|
|
|
|
/// </summary>
|
|
|
|
public event EventHandler<RoutedEventArgs>? VerticalSnapPointsChanged |
|
|
|
{ |
|
|
|
add => AddHandler(VerticalSnapPointsChangedEvent, value); |
|
|
|
remove => RemoveHandler(VerticalSnapPointsChangedEvent, value); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets whether the horizontal snap points for the <see cref="VirtualizingStackPanel"/> are equidistant from each other.
|
|
|
|
/// </summary>
|
|
|
|
public bool AreHorizontalSnapPointsRegular |
|
|
|
{ |
|
|
|
get { return GetValue(AreHorizontalSnapPointsRegularProperty); } |
|
|
|
set { SetValue(AreHorizontalSnapPointsRegularProperty, value); } |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets whether the vertical snap points for the <see cref="VirtualizingStackPanel"/> are equidistant from each other.
|
|
|
|
/// </summary>
|
|
|
|
public bool AreVerticalSnapPointsRegular |
|
|
|
{ |
|
|
|
get { return GetValue(AreVerticalSnapPointsRegularProperty); } |
|
|
|
set { SetValue(AreVerticalSnapPointsRegularProperty, value); } |
|
|
|
} |
|
|
|
|
|
|
|
protected override Size MeasureOverride(Size availableSize) |
|
|
|
{ |
|
|
|
if (!IsEffectivelyVisible) |
|
|
|
@ -145,6 +212,8 @@ namespace Avalonia.Controls |
|
|
|
finally |
|
|
|
{ |
|
|
|
_isInLayout = false; |
|
|
|
|
|
|
|
RaiseEvent(new RoutedEventArgs(Orientation == Orientation.Horizontal ? HorizontalSnapPointsChangedEvent : VerticalSnapPointsChangedEvent)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -622,6 +691,125 @@ namespace Avalonia.Controls |
|
|
|
Invalidate(c); |
|
|
|
} |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
public IReadOnlyList<double> GetIrregularSnapPoints(Orientation orientation, SnapPointsAlignment snapPointsAlignment) |
|
|
|
{ |
|
|
|
var snapPoints = new List<double>(); |
|
|
|
|
|
|
|
switch (orientation) |
|
|
|
{ |
|
|
|
case Orientation.Horizontal: |
|
|
|
if (AreHorizontalSnapPointsRegular) |
|
|
|
throw new InvalidOperationException(); |
|
|
|
if (Orientation == Orientation.Horizontal) |
|
|
|
{ |
|
|
|
foreach (var child in VisualChildren) |
|
|
|
{ |
|
|
|
double snapPoint = 0; |
|
|
|
|
|
|
|
switch (snapPointsAlignment) |
|
|
|
{ |
|
|
|
case SnapPointsAlignment.Near: |
|
|
|
snapPoint = child.Bounds.Left; |
|
|
|
break; |
|
|
|
case SnapPointsAlignment.Center: |
|
|
|
snapPoint = child.Bounds.Center.X; |
|
|
|
break; |
|
|
|
case SnapPointsAlignment.Far: |
|
|
|
snapPoint = child.Bounds.Right; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
snapPoints.Add(snapPoint); |
|
|
|
} |
|
|
|
} |
|
|
|
break; |
|
|
|
case Orientation.Vertical: |
|
|
|
if (AreVerticalSnapPointsRegular) |
|
|
|
throw new InvalidOperationException(); |
|
|
|
if (Orientation == Orientation.Vertical) |
|
|
|
{ |
|
|
|
foreach (var child in VisualChildren) |
|
|
|
{ |
|
|
|
double snapPoint = 0; |
|
|
|
|
|
|
|
switch (snapPointsAlignment) |
|
|
|
{ |
|
|
|
case SnapPointsAlignment.Near: |
|
|
|
snapPoint = child.Bounds.Top; |
|
|
|
break; |
|
|
|
case SnapPointsAlignment.Center: |
|
|
|
snapPoint = child.Bounds.Center.Y; |
|
|
|
break; |
|
|
|
case SnapPointsAlignment.Far: |
|
|
|
snapPoint = child.Bounds.Bottom; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
snapPoints.Add(snapPoint); |
|
|
|
} |
|
|
|
} |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
return snapPoints; |
|
|
|
} |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
public double GetRegularSnapPoints(Orientation orientation, SnapPointsAlignment snapPointsAlignment, out double offset) |
|
|
|
{ |
|
|
|
offset = 0f; |
|
|
|
var firstRealizedChild = _realizedElements?.Elements.FirstOrDefault(); |
|
|
|
|
|
|
|
if (firstRealizedChild == null) |
|
|
|
{ |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
double snapPoint = 0; |
|
|
|
|
|
|
|
switch (Orientation) |
|
|
|
{ |
|
|
|
case Orientation.Horizontal: |
|
|
|
if (!AreHorizontalSnapPointsRegular) |
|
|
|
throw new InvalidOperationException(); |
|
|
|
|
|
|
|
snapPoint = firstRealizedChild.Bounds.Width; |
|
|
|
switch (snapPointsAlignment) |
|
|
|
{ |
|
|
|
case SnapPointsAlignment.Near: |
|
|
|
offset = 0; |
|
|
|
break; |
|
|
|
case SnapPointsAlignment.Center: |
|
|
|
offset = (firstRealizedChild.Bounds.Right - firstRealizedChild.Bounds.Left) / 2; |
|
|
|
break; |
|
|
|
case SnapPointsAlignment.Far: |
|
|
|
offset = firstRealizedChild.Bounds.Width; |
|
|
|
break; |
|
|
|
} |
|
|
|
break; |
|
|
|
case Orientation.Vertical: |
|
|
|
if (!AreVerticalSnapPointsRegular) |
|
|
|
throw new InvalidOperationException(); |
|
|
|
snapPoint = firstRealizedChild.Bounds.Height; |
|
|
|
switch (snapPointsAlignment) |
|
|
|
{ |
|
|
|
case SnapPointsAlignment.Near: |
|
|
|
offset = 0; |
|
|
|
break; |
|
|
|
case SnapPointsAlignment.Center: |
|
|
|
offset = (firstRealizedChild.Bounds.Bottom - firstRealizedChild.Bounds.Top) / 2; |
|
|
|
break; |
|
|
|
case SnapPointsAlignment.Far: |
|
|
|
offset = firstRealizedChild.Bounds.Height; |
|
|
|
break; |
|
|
|
} |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
return snapPoint; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Stores the realized element state for a <see cref="VirtualizingStackPanel"/>.
|
|
|
|
/// </summary>
|
|
|
|
|