diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index bc86558ab3..0cde765f76 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -6,6 +6,7 @@ using Avalonia.Input; using Avalonia.Input.GestureRecognizers; using Avalonia.Utilities; using Avalonia.VisualTree; +using System.Linq; namespace Avalonia.Controls.Presenters { @@ -19,44 +20,34 @@ namespace Avalonia.Controls.Presenters /// /// Defines the property. /// - public static readonly DirectProperty CanHorizontallyScrollProperty = - AvaloniaProperty.RegisterDirect( - nameof(CanHorizontallyScroll), - o => o.CanHorizontallyScroll, - (o, v) => o.CanHorizontallyScroll = v); + public static readonly StyledProperty CanHorizontallyScrollProperty = + AvaloniaProperty.Register(nameof(CanHorizontallyScroll)); /// /// Defines the property. /// - public static readonly DirectProperty CanVerticallyScrollProperty = - AvaloniaProperty.RegisterDirect( - nameof(CanVerticallyScroll), - o => o.CanVerticallyScroll, - (o, v) => o.CanVerticallyScroll = v); + public static readonly StyledProperty CanVerticallyScrollProperty = + AvaloniaProperty.Register(nameof(CanVerticallyScroll)); /// /// Defines the property. /// public static readonly DirectProperty ExtentProperty = ScrollViewer.ExtentProperty.AddOwner( - o => o.Extent, - (o, v) => o.Extent = v); + o => o.Extent); /// /// Defines the property. /// - public static readonly DirectProperty OffsetProperty = - ScrollViewer.OffsetProperty.AddOwner( - o => o.Offset, - (o, v) => o.Offset = v); + public static readonly StyledProperty OffsetProperty = + ScrollViewer.OffsetProperty.AddOwner(new(coerce: ScrollViewer.CoerceOffset)); /// /// Defines the property. /// public static readonly DirectProperty ViewportProperty = ScrollViewer.ViewportProperty.AddOwner( - o => o.Viewport, - (o, v) => o.Viewport = v); + o => o.Viewport); /// /// Defines the property. @@ -88,11 +79,8 @@ namespace Avalonia.Controls.Presenters public static readonly StyledProperty IsScrollChainingEnabledProperty = ScrollViewer.IsScrollChainingEnabledProperty.AddOwner(); - private bool _canHorizontallyScroll; - private bool _canVerticallyScroll; private bool _arranging; private Size _extent; - private Vector _offset; private IDisposable? _logicalScrollSubscription; private Size _viewport; private Dictionary? _activeLogicalGestureScrolls; @@ -109,6 +97,7 @@ namespace Avalonia.Controls.Presenters private double _verticalSnapPoint; private double _verticalSnapPointOffset; private double _horizontalSnapPointOffset; + private CompositeDisposable? _ownerSubscriptions; /// /// Initializes static members of the class. @@ -116,7 +105,6 @@ namespace Avalonia.Controls.Presenters static ScrollContentPresenter() { ClipToBoundsProperty.OverrideDefaultValue(typeof(ScrollContentPresenter), true); - ChildProperty.Changed.AddClassHandler((x, e) => x.ChildChanged(e)); } /// @@ -137,8 +125,8 @@ namespace Avalonia.Controls.Presenters /// public bool CanHorizontallyScroll { - get { return _canHorizontallyScroll; } - set { SetAndRaise(CanHorizontallyScrollProperty, ref _canHorizontallyScroll, value); } + get => GetValue(CanHorizontallyScrollProperty); + set => SetValue(CanHorizontallyScrollProperty, value); } /// @@ -146,8 +134,8 @@ namespace Avalonia.Controls.Presenters /// public bool CanVerticallyScroll { - get { return _canVerticallyScroll; } - set { SetAndRaise(CanVerticallyScrollProperty, ref _canVerticallyScroll, value); } + get => GetValue(CanVerticallyScrollProperty); + set => SetValue(CanVerticallyScrollProperty, value); } /// @@ -164,8 +152,8 @@ namespace Avalonia.Controls.Presenters /// public Vector Offset { - get { return _offset; } - set { SetAndRaise(OffsetProperty, ref _offset, ScrollViewer.CoerceOffset(Extent, Viewport, value)); } + get => GetValue(OffsetProperty); + set => SetValue(OffsetProperty, value); } /// @@ -295,12 +283,64 @@ namespace Avalonia.Controls.Presenters if (result) { - Offset = offset; + SetCurrentValue(OffsetProperty, offset); } return result; } + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + AttachToScrollViewer(); + } + + /// + /// Locates the first ancestor and binds to it. Properties which have been set through other means are not bound. + /// + /// + /// This method is automatically called when the control is attached to a visual tree. + /// + protected internal virtual void AttachToScrollViewer() + { + _ownerSubscriptions?.Dispose(); + + var owner = this.FindAncestorOfType(); + + if (owner == null) + { + return; + } + + var subscriptionDisposables = new IDisposable?[] + { + IfUnset(CanHorizontallyScrollProperty, p => Bind(p, owner.GetObservable(ScrollViewer.HorizontalScrollBarVisibilityProperty).Select(NotDisabled), Data.BindingPriority.Template)), + IfUnset(CanVerticallyScrollProperty, p => Bind(p, owner.GetObservable(ScrollViewer.VerticalScrollBarVisibilityProperty).Select(NotDisabled), Data.BindingPriority.Template)), + IfUnset(OffsetProperty, p => new CompositeDisposable( + Bind(p, owner.GetBindingObservable(ScrollViewer.OffsetProperty), Data.BindingPriority.Template), + this.GetObservable(OffsetProperty).Subscribe(v => owner.SetCurrentValue(OffsetProperty, v)))), + IfUnset(IsScrollChainingEnabledProperty, p => Bind(p, owner.GetBindingObservable(ScrollViewer.IsScrollChainingEnabledProperty), Data.BindingPriority.Template)), + IfUnset(ContentProperty, p => Bind(p, owner.GetBindingObservable(ContentProperty), Data.BindingPriority.Template)), + + // read-only properties on ScrollViewer with internal setters: + this.GetObservable(ExtentProperty).Subscribe(v => owner.Extent = v), + this.GetObservable(ViewportProperty).Subscribe(v => owner.Viewport = v) + }.Where(d => d != null).Cast().ToArray(); + + _ownerSubscriptions = new CompositeDisposable(subscriptionDisposables); + + static bool NotDisabled(ScrollBarVisibility v) => v != ScrollBarVisibility.Disabled; + + IDisposable? IfUnset(T property, Func func) where T : AvaloniaProperty => GetValueStore().IsSet(property) ? null : func(property); + } + + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + _ownerSubscriptions?.Dispose(); + base.OnDetachedFromVisualTree(e); + } + /// void IScrollAnchorProvider.RegisterAnchorCandidate(Control element) { @@ -410,7 +450,7 @@ namespace Avalonia.Controls.Presenters try { _arranging = true; - Offset = newOffset; + SetCurrentValue(OffsetProperty, newOffset); } finally { @@ -427,7 +467,6 @@ namespace Avalonia.Controls.Presenters Viewport = finalSize; Extent = Child!.Bounds.Size.Inflate(Child.Margin); - Offset = ScrollViewer.CoerceOffset(Extent, finalSize, Offset); _isAnchorElementDirty = true; return finalSize; @@ -516,7 +555,7 @@ namespace Avalonia.Controls.Presenters } bool offsetChanged = newOffset != Offset; - Offset = newOffset; + SetCurrentValue(OffsetProperty, newOffset); e.Handled = !IsScrollChainingEnabled || offsetChanged; @@ -529,7 +568,7 @@ namespace Avalonia.Controls.Presenters _activeLogicalGestureScrolls?.Remove(e.Id); _scrollGestureSnapPoints?.Remove(e.Id); - Offset = SnapOffset(Offset); + SetCurrentValue(OffsetProperty, SnapOffset(Offset)); } private void OnScrollGestureInertiaStartingEnded(object? sender, ScrollGestureInertiaStartingEventArgs e) @@ -623,7 +662,7 @@ namespace Avalonia.Controls.Presenters Vector newOffset = SnapOffset(new Vector(x, y)); bool offsetChanged = newOffset != Offset; - Offset = newOffset; + SetCurrentValue(OffsetProperty, newOffset); e.Handled = !IsScrollChainingEnabled || offsetChanged; } @@ -651,6 +690,10 @@ namespace Avalonia.Controls.Presenters UpdateSnapPoints(); } + else if (change.Property == ChildProperty) + { + ChildChanged(change); + } else if (change.Property == HorizontalSnapPointsAlignmentProperty || change.Property == VerticalSnapPointsAlignmentProperty) { @@ -677,7 +720,7 @@ namespace Avalonia.Controls.Presenters if (e.OldValue != null) { - Offset = default; + SetCurrentValue(OffsetProperty, default); } } @@ -719,14 +762,14 @@ namespace Avalonia.Controls.Presenters if (logicalScroll != scrollable.IsLogicalScrollEnabled) { UpdateScrollableSubscription(Child); - Offset = default; + SetCurrentValue(OffsetProperty, default); InvalidateMeasure(); } else if (scrollable.IsLogicalScrollEnabled) { Viewport = scrollable.Viewport; Extent = scrollable.Extent; - Offset = scrollable.Offset; + SetCurrentValue(OffsetProperty, scrollable.Offset); } } diff --git a/src/Avalonia.Controls/Primitives/RangeBase.cs b/src/Avalonia.Controls/Primitives/RangeBase.cs index 38d848d69b..fd9de47236 100644 --- a/src/Avalonia.Controls/Primitives/RangeBase.cs +++ b/src/Avalonia.Controls/Primitives/RangeBase.cs @@ -12,30 +12,22 @@ namespace Avalonia.Controls.Primitives /// /// Defines the property. /// - public static readonly DirectProperty MinimumProperty = - AvaloniaProperty.RegisterDirect( - nameof(Minimum), - o => o.Minimum, - (o, v) => o.Minimum = v); + public static readonly StyledProperty MinimumProperty = + AvaloniaProperty.Register(nameof(Minimum), coerce: CoerceMinimum); /// /// Defines the property. /// - public static readonly DirectProperty MaximumProperty = - AvaloniaProperty.RegisterDirect( - nameof(Maximum), - o => o.Maximum, - (o, v) => o.Maximum = v); + public static readonly StyledProperty MaximumProperty = + AvaloniaProperty.Register(nameof(Maximum), 100, coerce: CoerceMaximum); /// /// Defines the property. /// - public static readonly DirectProperty ValueProperty = - AvaloniaProperty.RegisterDirect( - nameof(Value), - o => o.Value, - (o, v) => o.Value = v, - defaultBindingMode: BindingMode.TwoWay); + public static readonly StyledProperty ValueProperty = + AvaloniaProperty.Register(nameof(Value), + defaultBindingMode: BindingMode.TwoWay, + coerce: CoerceValue); /// /// Defines the property. @@ -49,44 +41,26 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty LargeChangeProperty = AvaloniaProperty.Register(nameof(LargeChange), 10); - private double _minimum; - private double _maximum = 100.0; - private double _value; - /// - /// Initializes a new instance of the class. + /// Gets or sets the minimum value. /// - public RangeBase() + public double Minimum { + get => GetValue(MinimumProperty); + set => SetValue(MinimumProperty, value); } - /// - /// Gets or sets the minimum value. - /// - public double Minimum + private static double CoerceMinimum(AvaloniaObject sender, double value) { - get - { - return _minimum; - } + return ValidateDouble(value) ? value : sender.GetValue(MinimumProperty); + } - set + private void OnMinimumChanged() + { + if (IsInitialized) { - if (!ValidateDouble(value)) - { - return; - } - - if (IsInitialized) - { - SetAndRaise(MinimumProperty, ref _minimum, value); - Maximum = ValidateMaximum(Maximum); - Value = ValidateValue(Value); - } - else - { - SetAndRaise(MinimumProperty, ref _minimum, value); - } + CoerceValue(MaximumProperty); + CoerceValue(ValueProperty); } } @@ -95,28 +69,22 @@ namespace Avalonia.Controls.Primitives /// public double Maximum { - get - { - return _maximum; - } + get => GetValue(MaximumProperty); + set => SetValue(MaximumProperty, value); + } + + private static double CoerceMaximum(AvaloniaObject sender, double value) + { + return ValidateDouble(value) + ? Math.Max(value, sender.GetValue(MinimumProperty)) + : sender.GetValue(MaximumProperty); + } - set + private void OnMaximumChanged() + { + if (IsInitialized) { - if (!ValidateDouble(value)) - { - return; - } - - if (IsInitialized) - { - value = ValidateMaximum(value); - SetAndRaise(MaximumProperty, ref _maximum, value); - Value = ValidateValue(Value); - } - else - { - SetAndRaise(MaximumProperty, ref _maximum, value); - } + CoerceValue(ValueProperty); } } @@ -125,28 +93,15 @@ namespace Avalonia.Controls.Primitives /// public double Value { - get - { - return _value; - } + get => GetValue(ValueProperty); + set => SetValue(ValueProperty, value); + } - set - { - if (!ValidateDouble(value)) - { - return; - } - - if (IsInitialized) - { - value = ValidateValue(value); - SetAndRaise(ValueProperty, ref _value, value); - } - else - { - SetAndRaise(ValueProperty, ref _value, value); - } - } + private static double CoerceValue(AvaloniaObject sender, double value) + { + return ValidateDouble(value) + ? MathUtilities.Clamp(value, sender.GetValue(MinimumProperty), sender.GetValue(MaximumProperty)) + : sender.GetValue(ValueProperty); } public double SmallChange @@ -165,37 +120,31 @@ namespace Avalonia.Controls.Primitives { base.OnInitialized(); - Maximum = ValidateMaximum(Maximum); - Value = ValidateValue(Value); + CoerceValue(MaximumProperty); + CoerceValue(ValueProperty); } - /// - /// Checks if the double value is not infinity nor NaN. - /// - /// The value. - private static bool ValidateDouble(double value) + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { - return !double.IsInfinity(value) && !double.IsNaN(value); - } + base.OnPropertyChanged(change); - /// - /// Validates/coerces the property. - /// - /// The value. - /// The coerced value. - private double ValidateMaximum(double value) - { - return Math.Max(value, Minimum); + if (change.Property == MinimumProperty) + { + OnMinimumChanged(); + } + else if (change.Property == MaximumProperty) + { + OnMaximumChanged(); + } } /// - /// Validates/coerces the property. + /// Checks if the double value is not infinity nor NaN. /// /// The value. - /// The coerced value. - private double ValidateValue(double value) + private static bool ValidateDouble(double value) { - return MathUtilities.Clamp(value, Minimum, Maximum); + return !double.IsInfinity(value) && !double.IsNaN(value); } } } diff --git a/src/Avalonia.Controls/Primitives/ScrollBar.cs b/src/Avalonia.Controls/Primitives/ScrollBar.cs index b4a8408901..df69baae0a 100644 --- a/src/Avalonia.Controls/Primitives/ScrollBar.cs +++ b/src/Avalonia.Controls/Primitives/ScrollBar.cs @@ -6,6 +6,9 @@ using Avalonia.Layout; using Avalonia.Threading; using Avalonia.Controls.Metadata; using Avalonia.Automation.Peers; +using Avalonia.VisualTree; +using Avalonia.Reactive; +using System.Linq; namespace Avalonia.Controls.Primitives { @@ -80,6 +83,7 @@ namespace Avalonia.Controls.Primitives private Button? _pageDownButton; private DispatcherTimer? _timer; private bool _isExpanded; + private CompositeDisposable? _ownerSubscriptions; /// /// Initializes static members of the class. @@ -88,6 +92,8 @@ namespace Avalonia.Controls.Primitives { Thumb.DragDeltaEvent.AddClassHandler((x, e) => x.OnThumbDragDelta(e), RoutingStrategies.Bubble); Thumb.DragCompletedEvent.AddClassHandler((x, e) => x.OnThumbDragComplete(e), RoutingStrategies.Bubble); + + FocusableProperty.OverrideMetadata(new(false)); } /// @@ -178,7 +184,60 @@ namespace Avalonia.Controls.Primitives _ => throw new InvalidOperationException("Invalid value for ScrollBar.Visibility.") }; - SetValue(IsVisibleProperty, isVisible); + SetCurrentValue(IsVisibleProperty, isVisible); + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + AttachToScrollViewer(); + } + + /// + /// Locates the first ancestor and binds to its properties. Properties which have been set through other means are not bound. + /// + /// + /// This method is automatically called when the control is attached to a visual tree. + /// + protected internal virtual void AttachToScrollViewer() + { + _ownerSubscriptions?.Dispose(); + + var owner = this.FindAncestorOfType(); + + if (owner == null) + { + return; + } + + var visibilitySource = Orientation == Orientation.Horizontal ? ScrollViewer.HorizontalScrollBarVisibilityProperty : ScrollViewer.VerticalScrollBarVisibilityProperty; + + var subscriptionDisposables = new IDisposable?[] + { + IfUnset(MaximumProperty, p => Bind(p, owner.GetObservable(ScrollViewer.ScrollBarMaximumProperty).Select(ExtractOrdinate), BindingPriority.Template)), + IfUnset(ValueProperty, p => new CompositeDisposable( + Bind(p, owner.GetObservable(ScrollViewer.OffsetProperty).Select(ExtractOrdinate), BindingPriority.Template), + this.GetObservable(ValueProperty).Subscribe(v => SetScrollViewerOffset(owner, v)))), + IfUnset(ViewportSizeProperty, p => Bind(p, owner.GetObservable(ScrollViewer.ViewportProperty).Select(ExtractOrdinate), BindingPriority.Template)), + IfUnset(VisibilityProperty, p => Bind(p, owner.GetObservable(visibilitySource), BindingPriority.Template)), + IfUnset(AllowAutoHideProperty, p => Bind(p, owner.GetObservable(ScrollViewer.AllowAutoHideProperty), BindingPriority.Template)), + IfUnset(LargeChangeProperty, p => Bind(p, owner.GetObservable(ScrollViewer.LargeChangeProperty).Select(ExtractOrdinate), BindingPriority.Template)), + IfUnset(SmallChangeProperty, p => Bind(p, owner.GetObservable(ScrollViewer.SmallChangeProperty).Select(ExtractOrdinate), BindingPriority.Template)) + }.Where(d => d != null).Cast().ToArray(); + + _ownerSubscriptions = new CompositeDisposable(subscriptionDisposables); + + IDisposable? IfUnset(T property, Func func) where T : AvaloniaProperty => GetValueStore().IsSet(property) ? null : func(property); + } + + private double ExtractOrdinate(Vector v) => Orientation == Orientation.Horizontal ? v.X : v.Y; + private double ExtractOrdinate(Size v) => Orientation == Orientation.Horizontal ? v.Width : v.Height; + private void SetScrollViewerOffset(ScrollViewer viewer, double value) => viewer.SetCurrentValue(ScrollViewer.OffsetProperty, Orientation == Orientation.Horizontal ? viewer.Offset.WithX(value) : viewer.Offset.WithY(value)); + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + _ownerSubscriptions?.Dispose(); + base.OnDetachedFromVisualTree(e); } protected override void OnKeyDown(KeyEventArgs e) @@ -202,6 +261,7 @@ namespace Avalonia.Controls.Primitives if (change.Property == OrientationProperty) { UpdatePseudoClasses(change.GetNewValue()); + AttachToScrollViewer(); // there's no way to manually refresh bindings, so reapply them } else if (change.Property == AllowAutoHideProperty) { @@ -373,25 +433,25 @@ namespace Avalonia.Controls.Primitives private void SmallDecrement() { - Value = Math.Max(Value - SmallChange, Minimum); + SetCurrentValue(ValueProperty, Math.Max(Value - SmallChange, Minimum)); OnScroll(ScrollEventType.SmallDecrement); } private void SmallIncrement() { - Value = Math.Min(Value + SmallChange, Maximum); + SetCurrentValue(ValueProperty, Math.Min(Value + SmallChange, Maximum)); OnScroll(ScrollEventType.SmallIncrement); } private void LargeDecrement() { - Value = Math.Max(Value - LargeChange, Minimum); + SetCurrentValue(ValueProperty, Math.Max(Value - LargeChange, Minimum)); OnScroll(ScrollEventType.LargeDecrement); } private void LargeIncrement() { - Value = Math.Min(Value + LargeChange, Maximum); + SetCurrentValue(ValueProperty, Math.Min(Value + LargeChange, Maximum)); OnScroll(ScrollEventType.LargeIncrement); } diff --git a/src/Avalonia.Controls/Primitives/Track.cs b/src/Avalonia.Controls/Primitives/Track.cs index 9e8d1478fa..fe4912a33c 100644 --- a/src/Avalonia.Controls/Primitives/Track.cs +++ b/src/Avalonia.Controls/Primitives/Track.cs @@ -15,14 +15,14 @@ namespace Avalonia.Controls.Primitives [PseudoClasses(":vertical", ":horizontal")] public class Track : Control { - public static readonly DirectProperty MinimumProperty = - RangeBase.MinimumProperty.AddOwner(o => o.Minimum, (o, v) => o.Minimum = v); + public static readonly StyledProperty MinimumProperty = + RangeBase.MinimumProperty.AddOwner(); - public static readonly DirectProperty MaximumProperty = - RangeBase.MaximumProperty.AddOwner(o => o.Maximum, (o, v) => o.Maximum = v); + public static readonly StyledProperty MaximumProperty = + RangeBase.MaximumProperty.AddOwner(); - public static readonly DirectProperty ValueProperty = - RangeBase.ValueProperty.AddOwner(o => o.Value, (o, v) => o.Value = v); + public static readonly StyledProperty ValueProperty = + RangeBase.ValueProperty.AddOwner(); public static readonly StyledProperty ViewportSizeProperty = ScrollBar.ViewportSizeProperty.AddOwner(); @@ -45,10 +45,6 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty IgnoreThumbDragProperty = AvaloniaProperty.Register(nameof(IgnoreThumbDrag)); - private double _minimum; - private double _maximum = 100.0; - private double _value; - static Track() { ThumbProperty.Changed.AddClassHandler((x, e) => x.ThumbChanged(e)); @@ -64,20 +60,20 @@ namespace Avalonia.Controls.Primitives public double Minimum { - get { return _minimum; } - set { SetAndRaise(MinimumProperty, ref _minimum, value); } + get => GetValue(MinimumProperty); + set => SetValue(MinimumProperty, value); } public double Maximum { - get { return _maximum; } - set { SetAndRaise(MaximumProperty, ref _maximum, value); } + get => GetValue(MaximumProperty); + set => SetValue(MaximumProperty, value); } public double Value { - get { return _value; } - set { SetAndRaise(ValueProperty, ref _value, value); } + get => GetValue(ValueProperty); + set => SetValue(ValueProperty, value); } public double ViewportSize @@ -443,11 +439,11 @@ namespace Avalonia.Controls.Primitives { if (IgnoreThumbDrag) return; - - Value = MathUtilities.Clamp( + + SetCurrentValue(ValueProperty, MathUtilities.Clamp( Value + ValueFromDistance(e.Vector.X, e.Vector.Y), Minimum, - Maximum); + Maximum)); } private void ShowChildren(bool visible) diff --git a/src/Avalonia.Controls/ProgressBar.cs b/src/Avalonia.Controls/ProgressBar.cs index 98a9ec60bf..daf6be12d2 100644 --- a/src/Avalonia.Controls/ProgressBar.cs +++ b/src/Avalonia.Controls/ProgressBar.cs @@ -100,8 +100,6 @@ namespace Avalonia.Controls } private double _percentage; - private double _indeterminateStartingOffset; - private double _indeterminateEndingOffset; private Border? _indicator; private IDisposable? _trackSizeChangedListener; @@ -122,17 +120,11 @@ namespace Avalonia.Controls nameof(Percentage), o => o.Percentage); - public static readonly DirectProperty IndeterminateStartingOffsetProperty = - AvaloniaProperty.RegisterDirect( - nameof(IndeterminateStartingOffset), - p => p.IndeterminateStartingOffset, - (p, o) => p.IndeterminateStartingOffset = o); + public static readonly StyledProperty IndeterminateStartingOffsetProperty = + AvaloniaProperty.Register(nameof(IndeterminateStartingOffset)); - public static readonly DirectProperty IndeterminateEndingOffsetProperty = - AvaloniaProperty.RegisterDirect( - nameof(IndeterminateEndingOffset), - p => p.IndeterminateEndingOffset, - (p, o) => p.IndeterminateEndingOffset = o); + public static readonly StyledProperty IndeterminateEndingOffsetProperty = + AvaloniaProperty.Register(nameof(IndeterminateEndingOffset)); public double Percentage { @@ -142,19 +134,19 @@ namespace Avalonia.Controls public double IndeterminateStartingOffset { - get => _indeterminateStartingOffset; - set => SetAndRaise(IndeterminateStartingOffsetProperty, ref _indeterminateStartingOffset, value); + get => GetValue(IndeterminateStartingOffsetProperty); + set => SetValue(IndeterminateStartingOffsetProperty, value); } public double IndeterminateEndingOffset { - get => _indeterminateEndingOffset; - set => SetAndRaise(IndeterminateEndingOffsetProperty, ref _indeterminateEndingOffset, value); + get => GetValue(IndeterminateEndingOffsetProperty); + set => SetValue(IndeterminateEndingOffsetProperty, value); } static ProgressBar() { - ValueProperty.OverrideMetadata(new DirectPropertyMetadata(defaultBindingMode: BindingMode.OneWay)); + ValueProperty.OverrideMetadata(new(defaultBindingMode: BindingMode.OneWay)); ValueProperty.Changed.AddClassHandler((x, e) => x.UpdateIndicatorWhenPropChanged(e)); MinimumProperty.Changed.AddClassHandler((x, e) => x.UpdateIndicatorWhenPropChanged(e)); MaximumProperty.Changed.AddClassHandler((x, e) => x.UpdateIndicatorWhenPropChanged(e)); @@ -261,8 +253,8 @@ namespace Avalonia.Controls // Remove these properties when we switch to fluent as default and removed the old one. - IndeterminateStartingOffset = -dim; - IndeterminateEndingOffset = dim; + SetCurrentValue(IndeterminateStartingOffsetProperty,-dim); + SetCurrentValue(IndeterminateEndingOffsetProperty,dim); var padding = Padding; var rectangle = new RectangleGeometry( diff --git a/src/Avalonia.Controls/ScrollViewer.cs b/src/Avalonia.Controls/ScrollViewer.cs index 0f1b8f388c..f469e4948d 100644 --- a/src/Avalonia.Controls/ScrollViewer.cs +++ b/src/Avalonia.Controls/ScrollViewer.cs @@ -16,54 +16,25 @@ namespace Avalonia.Controls [TemplatePart("PART_VerticalScrollBar", typeof(ScrollBar))] public class ScrollViewer : ContentControl, IScrollable, IScrollAnchorProvider { - /// - /// Defines the property. - /// - /// - /// There is no public C# accessor for this property as it is intended to be bound to by a - /// in the control's template. - /// - public static readonly DirectProperty CanHorizontallyScrollProperty = - AvaloniaProperty.RegisterDirect( - nameof(CanHorizontallyScroll), - o => o.CanHorizontallyScroll); - - /// - /// Defines the property. - /// - /// - /// There is no public C# accessor for this property as it is intended to be bound to by a - /// in the control's template. - /// - public static readonly DirectProperty CanVerticallyScrollProperty = - AvaloniaProperty.RegisterDirect( - nameof(CanVerticallyScroll), - o => o.CanVerticallyScroll); - /// /// Defines the property. /// public static readonly DirectProperty ExtentProperty = AvaloniaProperty.RegisterDirect(nameof(Extent), - o => o.Extent, - (o, v) => o.Extent = v); + o => o.Extent); /// /// Defines the property. /// - public static readonly DirectProperty OffsetProperty = - AvaloniaProperty.RegisterDirect( - nameof(Offset), - o => o.Offset, - (o, v) => o.Offset = v); + public static readonly StyledProperty OffsetProperty = + AvaloniaProperty.Register(nameof(Offset), coerce: CoerceOffset); /// /// Defines the property. /// public static readonly DirectProperty ViewportProperty = AvaloniaProperty.RegisterDirect(nameof(Viewport), - o => o.Viewport, - (o, v) => o.Viewport = v); + o => o.Viewport); /// /// Defines the property. @@ -82,41 +53,12 @@ namespace Avalonia.Controls o => o.SmallChange); /// - /// Defines the HorizontalScrollBarMaximum property. - /// - /// - /// There is no public C# accessor for this property as it is intended to be bound to by a - /// in the control's template. - /// - public static readonly DirectProperty HorizontalScrollBarMaximumProperty = - AvaloniaProperty.RegisterDirect( - nameof(HorizontalScrollBarMaximum), - o => o.HorizontalScrollBarMaximum); - - /// - /// Defines the HorizontalScrollBarValue property. + /// Defines the property. /// - /// - /// There is no public C# accessor for this property as it is intended to be bound to by a - /// in the control's template. - /// - public static readonly DirectProperty HorizontalScrollBarValueProperty = - AvaloniaProperty.RegisterDirect( - nameof(HorizontalScrollBarValue), - o => o.HorizontalScrollBarValue, - (o, v) => o.HorizontalScrollBarValue = v); - - /// - /// Defines the HorizontalScrollBarViewportSize property. - /// - /// - /// There is no public C# accessor for this property as it is intended to be bound to by a - /// in the control's template. - /// - public static readonly DirectProperty HorizontalScrollBarViewportSizeProperty = - AvaloniaProperty.RegisterDirect( - nameof(HorizontalScrollBarViewportSize), - o => o.HorizontalScrollBarViewportSize); + public static readonly DirectProperty ScrollBarMaximumProperty = + AvaloniaProperty.RegisterDirect( + nameof(ScrollBarMaximum), + o => o.ScrollBarMaximum); /// /// Defines the property. @@ -126,31 +68,6 @@ namespace Avalonia.Controls nameof(HorizontalScrollBarVisibility), ScrollBarVisibility.Disabled); - /// - /// Defines the VerticalScrollBarMaximum property. - /// - /// - /// There is no public C# accessor for this property as it is intended to be bound to by a - /// in the control's template. - /// - public static readonly DirectProperty VerticalScrollBarMaximumProperty = - AvaloniaProperty.RegisterDirect( - nameof(VerticalScrollBarMaximum), - o => o.VerticalScrollBarMaximum); - - /// - /// Defines the VerticalScrollBarValue property. - /// - /// - /// There is no public C# accessor for this property as it is intended to be bound to by a - /// in the control's template. - /// - public static readonly DirectProperty VerticalScrollBarValueProperty = - AvaloniaProperty.RegisterDirect( - nameof(VerticalScrollBarValue), - o => o.VerticalScrollBarValue, - (o, v) => o.VerticalScrollBarValue = v); - /// /// Defines the property. /// @@ -179,18 +96,6 @@ namespace Avalonia.Controls AvaloniaProperty.RegisterAttached( nameof(VerticalSnapPointsAlignment)); - /// - /// Defines the VerticalScrollBarViewportSize property. - /// - /// - /// There is no public C# accessor for this property as it is intended to be bound to by a - /// in the control's template. - /// - public static readonly DirectProperty VerticalScrollBarViewportSizeProperty = - AvaloniaProperty.RegisterDirect( - nameof(VerticalScrollBarViewportSize), - o => o.VerticalScrollBarViewportSize); - /// /// Defines the property. /// @@ -242,25 +147,16 @@ namespace Avalonia.Controls private IDisposable? _childSubscription; private ILogicalScrollable? _logicalScrollable; private Size _extent; - private Vector _offset; private Size _viewport; private Size _oldExtent; private Vector _oldOffset; + private Vector _oldMaximum; private Size _oldViewport; private Size _largeChange; private Size _smallChange = new Size(DefaultSmallChange, DefaultSmallChange); private bool _isExpanded; private IDisposable? _scrollBarExpandSubscription; - /// - /// Initializes static members of the class. - /// - static ScrollViewer() - { - HorizontalScrollBarVisibilityProperty.Changed.AddClassHandler((x, e) => x.ScrollBarVisibilityChanged(e)); - VerticalScrollBarVisibilityProperty.Changed.AddClassHandler((x, e) => x.ScrollBarVisibilityChanged(e)); - } - /// /// Initializes a new instance of the class. /// @@ -288,7 +184,7 @@ namespace Avalonia.Controls return _extent; } - private set + internal set { if (SetAndRaise(ExtentProperty, ref _extent, value)) { @@ -302,18 +198,8 @@ namespace Avalonia.Controls /// public Vector Offset { - get - { - return _offset; - } - - set - { - if (SetAndRaise(OffsetProperty, ref _offset, CoerceOffset(Extent, Viewport, value))) - { - CalculatedPropertiesChanged(); - } - } + get => GetValue(OffsetProperty); + set => SetValue(OffsetProperty, value); } /// @@ -326,7 +212,7 @@ namespace Avalonia.Controls return _viewport; } - private set + internal set { if (SetAndRaise(ViewportProperty, ref _viewport, value)) { @@ -383,70 +269,9 @@ namespace Avalonia.Controls public Control? CurrentAnchor => (Presenter as IScrollAnchorProvider)?.CurrentAnchor; /// - /// Gets the maximum horizontal scrollbar value. + /// Gets the maximum scrolling distance (which is - ). /// - protected double HorizontalScrollBarMaximum - { - get { return Max(_extent.Width - _viewport.Width, 0); } - } - - /// - /// Gets or sets the horizontal scrollbar value. - /// - protected double HorizontalScrollBarValue - { - get { return _offset.X; } - set - { - if (_offset.X != value) - { - var old = Offset.X; - Offset = Offset.WithX(value); - RaisePropertyChanged(HorizontalScrollBarValueProperty, old, value); - } - } - } - - /// - /// Gets the size of the horizontal scrollbar viewport. - /// - protected double HorizontalScrollBarViewportSize - { - get { return _viewport.Width; } - } - - /// - /// Gets the maximum vertical scrollbar value. - /// - protected double VerticalScrollBarMaximum - { - get { return Max(_extent.Height - _viewport.Height, 0); } - } - - /// - /// Gets or sets the vertical scrollbar value. - /// - protected double VerticalScrollBarValue - { - get { return _offset.Y; } - set - { - if (_offset.Y != value) - { - var old = Offset.Y; - Offset = Offset.WithY(value); - RaisePropertyChanged(VerticalScrollBarValueProperty, old, value); - } - } - } - - /// - /// Gets the size of the vertical scrollbar viewport. - /// - protected double VerticalScrollBarViewportSize - { - get { return _viewport.Height; } - } + public Vector ScrollBarMaximum => new(Max(_extent.Width - _viewport.Width, 0), Max(_extent.Height - _viewport.Height, 0)); /// /// Gets a value that indicates whether any scrollbar is expanded. @@ -528,82 +353,52 @@ namespace Avalonia.Controls /// /// Scrolls the content up one line. /// - public void LineUp() - { - Offset -= new Vector(0, _smallChange.Height); - } + public void LineUp() => SetCurrentValue(OffsetProperty, Offset - new Vector(0, _smallChange.Height)); /// /// Scrolls the content down one line. /// - public void LineDown() - { - Offset += new Vector(0, _smallChange.Height); - } + public void LineDown() => SetCurrentValue(OffsetProperty, Offset + new Vector(0, _smallChange.Height)); /// /// Scrolls the content left one line. /// - public void LineLeft() - { - Offset -= new Vector(_smallChange.Width, 0); - } + public void LineLeft() => SetCurrentValue(OffsetProperty, Offset - new Vector(_smallChange.Width, 0)); /// /// Scrolls the content right one line. /// - public void LineRight() - { - Offset += new Vector(_smallChange.Width, 0); - } + public void LineRight() => SetCurrentValue(OffsetProperty, Offset + new Vector(_smallChange.Width, 0)); /// /// Scrolls the content upward by one page. /// - public void PageUp() - { - VerticalScrollBarValue = Math.Max(_offset.Y - _viewport.Height, 0); - } + public void PageUp() => SetCurrentValue(OffsetProperty, Offset.WithY(Math.Max(Offset.Y - _viewport.Height, 0))); /// /// Scrolls the content downward by one page. /// - public void PageDown() - { - VerticalScrollBarValue = Math.Min(_offset.Y + _viewport.Height, VerticalScrollBarMaximum); - } + public void PageDown() => SetCurrentValue(OffsetProperty, Offset.WithY(Math.Min(Offset.Y + _viewport.Height, ScrollBarMaximum.Y))); /// /// Scrolls the content left by one page. /// - public void PageLeft() - { - HorizontalScrollBarValue = Math.Max(_offset.X - _viewport.Width, 0); - } + public void PageLeft() => SetCurrentValue(OffsetProperty, Offset.WithX(Math.Max(Offset.X - _viewport.Width, 0))); /// /// Scrolls the content tight by one page. /// - public void PageRight() - { - HorizontalScrollBarValue = Math.Min(_offset.X + _viewport.Width, HorizontalScrollBarMaximum); - } + public void PageRight() => SetCurrentValue(OffsetProperty, Offset.WithX(Math.Min(Offset.X + _viewport.Width, ScrollBarMaximum.X))); /// /// Scrolls to the top-left corner of the content. /// - public void ScrollToHome() - { - Offset = new Vector(double.NegativeInfinity, double.NegativeInfinity); - } + public void ScrollToHome() => SetCurrentValue(OffsetProperty, new Vector(double.NegativeInfinity, double.NegativeInfinity)); /// /// Scrolls to the bottom-left corner of the content. /// - public void ScrollToEnd() - { - Offset = new Vector(double.NegativeInfinity, double.PositiveInfinity); - } + public void ScrollToEnd() => SetCurrentValue(OffsetProperty, new Vector(double.NegativeInfinity, double.PositiveInfinity)); /// /// Gets the value of the HorizontalScrollBarVisibility attached property. @@ -819,11 +614,14 @@ namespace Avalonia.Controls return false; } - internal static Vector CoerceOffset(Size extent, Size viewport, Vector offset) + internal static Vector CoerceOffset(AvaloniaObject sender, Vector value) { + var extent = sender.GetValue(ExtentProperty); + var viewport = sender.GetValue(ViewportProperty); + var maxX = Math.Max(extent.Width - viewport.Width, 0); var maxY = Math.Max(extent.Height - viewport.Height, 0); - return new Vector(Clamp(offset.X, 0, maxX), Clamp(offset.Y, 0, maxY)); + return new Vector(Clamp(value.X, 0, maxX), Clamp(value.Y, 0, maxY)); } private static double Clamp(double value, double min, double max) @@ -859,40 +657,11 @@ namespace Avalonia.Controls CalculatedPropertiesChanged(); } - private void ScrollBarVisibilityChanged(AvaloniaPropertyChangedEventArgs e) - { - var wasEnabled = e.OldValue.GetValueOrDefault() != ScrollBarVisibility.Disabled; - var isEnabled = e.NewValue.GetValueOrDefault() != ScrollBarVisibility.Disabled; - - if (wasEnabled != isEnabled) - { - if (e.Property == HorizontalScrollBarVisibilityProperty) - { - RaisePropertyChanged( - CanHorizontallyScrollProperty, - wasEnabled, - isEnabled); - } - else if (e.Property == VerticalScrollBarVisibilityProperty) - { - RaisePropertyChanged( - CanVerticallyScrollProperty, - wasEnabled, - isEnabled); - } - } - } - private void CalculatedPropertiesChanged() { - // Pass old values of 0 here because we don't have the old values at this point, - // and it shouldn't matter as only the template uses these properties. - RaisePropertyChanged(HorizontalScrollBarMaximumProperty, 0, HorizontalScrollBarMaximum); - RaisePropertyChanged(HorizontalScrollBarValueProperty, 0, HorizontalScrollBarValue); - RaisePropertyChanged(HorizontalScrollBarViewportSizeProperty, 0, HorizontalScrollBarViewportSize); - RaisePropertyChanged(VerticalScrollBarMaximumProperty, 0, VerticalScrollBarMaximum); - RaisePropertyChanged(VerticalScrollBarValueProperty, 0, VerticalScrollBarValue); - RaisePropertyChanged(VerticalScrollBarViewportSizeProperty, 0, VerticalScrollBarViewportSize); + var newMaximum = ScrollBarMaximum; + RaisePropertyChanged(ScrollBarMaximumProperty, _oldMaximum, newMaximum); + _oldMaximum = newMaximum; if (_logicalScrollable?.IsLogicalScrollEnabled == true) { @@ -906,6 +675,16 @@ namespace Avalonia.Controls } } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == OffsetProperty) + { + CalculatedPropertiesChanged(); + } + } + protected override void OnKeyDown(KeyEventArgs e) { if (e.Key == Key.PageUp) diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index 7e43f6682c..ed9075c155 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -110,7 +110,7 @@ namespace Avalonia.Controls Thumb.DragCompletedEvent.AddClassHandler((x, e) => x.OnThumbDragCompleted(e), RoutingStrategies.Bubble); - ValueProperty.OverrideMetadata(new DirectPropertyMetadata(enableDataValidation: true)); + ValueProperty.OverrideMetadata(new(enableDataValidation: true)); AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue(AutomationControlType.Slider); } diff --git a/src/Avalonia.Themes.Fluent/Controls/MenuScrollViewer.xaml b/src/Avalonia.Themes.Fluent/Controls/MenuScrollViewer.xaml index e8508935d2..adca099a10 100644 --- a/src/Avalonia.Themes.Fluent/Controls/MenuScrollViewer.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/MenuScrollViewer.xaml @@ -75,17 +75,10 @@ Height="20" /> - + - + diff --git a/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml b/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml index 7a8c15bf60..04b8f80f82 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml @@ -29,46 +29,23 @@ Grid.RowSpan="2" Grid.ColumnSpan="2" Background="{TemplateBinding Background}" - CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}" - CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}" - Content="{TemplateBinding Content}" - Extent="{TemplateBinding Extent, Mode=TwoWay}" - Padding="{TemplateBinding Padding}" HorizontalSnapPointsType="{TemplateBinding HorizontalSnapPointsType}" VerticalSnapPointsType="{TemplateBinding VerticalSnapPointsType}" HorizontalSnapPointsAlignment="{TemplateBinding HorizontalSnapPointsAlignment}" VerticalSnapPointsAlignment="{TemplateBinding VerticalSnapPointsAlignment}" - Offset="{TemplateBinding Offset, Mode=TwoWay}" - Viewport="{TemplateBinding Viewport, Mode=TwoWay}" - IsScrollChainingEnabled="{TemplateBinding IsScrollChainingEnabled}"> + Padding="{TemplateBinding Padding}"> - + + Grid.Row="1" /> + Grid.Column="1" /> + Background="{TemplateBinding Background}"> - + - - + + Orientation="Vertical"/> @@ -105,16 +79,7 @@ + Margin="{TemplateBinding Padding}" /> diff --git a/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs index 98a2d76b48..536c766a7d 100644 --- a/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs @@ -384,31 +384,17 @@ namespace Avalonia.Base.UnitTests.Layout new ScrollContentPresenter { Name = "PART_ContentPresenter", - [~ContentPresenter.ContentProperty] = control[~ContentControl.ContentProperty], - [~~ScrollContentPresenter.ExtentProperty] = control[~~ScrollViewer.ExtentProperty], - [~~ScrollContentPresenter.OffsetProperty] = control[~~ScrollViewer.OffsetProperty], - [~~ScrollContentPresenter.ViewportProperty] = control[~~ScrollViewer.ViewportProperty], - [~ScrollContentPresenter.CanHorizontallyScrollProperty] = control[~ScrollViewer.CanHorizontallyScrollProperty], - [~ScrollContentPresenter.CanVerticallyScrollProperty] = control[~ScrollViewer.CanVerticallyScrollProperty], }.RegisterInNameScope(scope), new ScrollBar { Name = "horizontalScrollBar", Orientation = Orientation.Horizontal, - [~RangeBase.MaximumProperty] = control[~ScrollViewer.HorizontalScrollBarMaximumProperty], - [~~RangeBase.ValueProperty] = control[~~ScrollViewer.HorizontalScrollBarValueProperty], - [~ScrollBar.ViewportSizeProperty] = control[~ScrollViewer.HorizontalScrollBarViewportSizeProperty], - [~ScrollBar.VisibilityProperty] = control[~ScrollViewer.HorizontalScrollBarVisibilityProperty], [Grid.RowProperty] = 1, }.RegisterInNameScope(scope), new ScrollBar { Name = "verticalScrollBar", Orientation = Orientation.Vertical, - [~RangeBase.MaximumProperty] = control[~ScrollViewer.VerticalScrollBarMaximumProperty], - [~~RangeBase.ValueProperty] = control[~~ScrollViewer.VerticalScrollBarValueProperty], - [~ScrollBar.ViewportSizeProperty] = control[~ScrollViewer.VerticalScrollBarViewportSizeProperty], - [~ScrollBar.VisibilityProperty] = control[~ScrollViewer.VerticalScrollBarVisibilityProperty], [Grid.ColumnProperty] = 1, }.RegisterInNameScope(scope), }, diff --git a/tests/Avalonia.Controls.UnitTests/CarouselTests.cs b/tests/Avalonia.Controls.UnitTests/CarouselTests.cs index c6237c2fa8..6624d13165 100644 --- a/tests/Avalonia.Controls.UnitTests/CarouselTests.cs +++ b/tests/Avalonia.Controls.UnitTests/CarouselTests.cs @@ -301,12 +301,6 @@ namespace Avalonia.Controls.UnitTests new ScrollContentPresenter { Name = "PART_ContentPresenter", - [~ScrollContentPresenter.ContentProperty] = parent.GetObservable(ScrollViewer.ContentProperty).ToBinding(), - [~~ScrollContentPresenter.ExtentProperty] = parent[~~ScrollViewer.ExtentProperty], - [~~ScrollContentPresenter.OffsetProperty] = parent[~~ScrollViewer.OffsetProperty], - [~~ScrollContentPresenter.ViewportProperty] = parent[~~ScrollViewer.ViewportProperty], - [~ScrollContentPresenter.CanHorizontallyScrollProperty] = parent[~ScrollViewer.CanHorizontallyScrollProperty], - [~ScrollContentPresenter.CanVerticallyScrollProperty] = parent[~ScrollViewer.CanVerticallyScrollProperty], }.RegisterInNameScope(scope), } }); diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index f42185a59c..7a227a48ab 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -590,18 +590,11 @@ namespace Avalonia.Controls.UnitTests new ScrollContentPresenter { Name = "PART_ContentPresenter", - [~ScrollContentPresenter.ContentProperty] = parent.GetObservable(ScrollViewer.ContentProperty).ToBinding(), - [~~ScrollContentPresenter.ExtentProperty] = parent[~~ScrollViewer.ExtentProperty], - [~~ScrollContentPresenter.OffsetProperty] = parent[~~ScrollViewer.OffsetProperty], - [~~ScrollContentPresenter.ViewportProperty] = parent[~~ScrollViewer.ViewportProperty], - [~ScrollContentPresenter.CanHorizontallyScrollProperty] = parent[~ScrollViewer.CanHorizontallyScrollProperty], - [~ScrollContentPresenter.CanVerticallyScrollProperty] = parent[~ScrollViewer.CanVerticallyScrollProperty], }.RegisterInNameScope(scope), new ScrollBar { Name = "verticalScrollBar", - [~ScrollBar.MaximumProperty] = parent[~ScrollViewer.VerticalScrollBarMaximumProperty], - [~~ScrollBar.ValueProperty] = parent[~~ScrollViewer.VerticalScrollBarValueProperty], + Orientation = Orientation.Vertical, } } }); diff --git a/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs b/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs index d3eb42f147..407bd40c37 100644 --- a/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs @@ -21,45 +21,20 @@ namespace Avalonia.Controls.UnitTests Content = "Foo", }; - target.ApplyTemplate(); - ((ContentPresenter)target.Presenter).UpdateChild(); + InitializeScrollViewer(target); Assert.IsType(target.Presenter.Child); } - [Fact] - public void CanHorizontallyScroll_Should_Track_HorizontalScrollBarVisibility() - { - var target = new ScrollViewer(); - var values = new List(); - - target.GetObservable(ScrollViewer.CanHorizontallyScrollProperty).Subscribe(x => values.Add(x)); - target.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled; - target.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto; - - Assert.Equal(new[] { false, true }, values); - } - - [Fact] - public void CanVerticallyScroll_Should_Track_VerticalScrollBarVisibility() - { - var target = new ScrollViewer(); - var values = new List(); - - target.GetObservable(ScrollViewer.CanVerticallyScrollProperty).Subscribe(x => values.Add(x)); - target.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled; - target.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; - - Assert.Equal(new[] { true, false, true }, values); - } - [Fact] public void Offset_Should_Be_Coerced_To_Viewport() { - var target = new ScrollViewer(); - target.SetValue(ScrollViewer.ExtentProperty, new Size(20, 20)); - target.SetValue(ScrollViewer.ViewportProperty, new Size(10, 10)); - target.Offset = new Vector(12, 12); + var target = new ScrollViewer + { + Extent = new Size(20, 20), + Viewport = new Size(10, 10), + Offset = new Vector(12, 12) + }; Assert.Equal(new Vector(10, 10), target.Offset); } @@ -67,10 +42,12 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Test_ScrollToHome() { - var target = new ScrollViewer(); - target.SetValue(ScrollViewer.ExtentProperty, new Size(50, 50)); - target.SetValue(ScrollViewer.ViewportProperty, new Size(10, 10)); - target.Offset = new Vector(25, 25); + var target = new ScrollViewer + { + Extent = new Size(50, 50), + Viewport = new Size(10, 10), + Offset = new Vector(25, 25) + }; target.ScrollToHome(); Assert.Equal(new Vector(0, 0), target.Offset); @@ -79,10 +56,12 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Test_ScrollToEnd() { - var target = new ScrollViewer(); - target.SetValue(ScrollViewer.ExtentProperty, new Size(50, 50)); - target.SetValue(ScrollViewer.ViewportProperty, new Size(10, 10)); - target.Offset = new Vector(25, 25); + var target = new ScrollViewer + { + Extent = new Size(50, 50), + Viewport = new Size(10, 10), + Offset = new Vector(25, 25) + }; target.ScrollToEnd(); Assert.Equal(new Vector(0, 40), target.Offset); @@ -99,9 +78,10 @@ namespace Avalonia.Controls.UnitTests [Fact] public void LargeChange_Should_Be_Viewport() { - var target = new ScrollViewer(); - - target.SetValue(ScrollViewer.ViewportProperty, new Size(104, 143)); + var target = new ScrollViewer + { + Viewport = new Size(104, 143) + }; Assert.Equal(new Size(104, 143), target.LargeChange); } @@ -120,8 +100,7 @@ namespace Avalonia.Controls.UnitTests Content = child.Object, }; - target.ApplyTemplate(); - ((ContentPresenter)target.Presenter).UpdateChild(); + InitializeScrollViewer(target); Assert.Equal(new Size(12, 43), target.SmallChange); } @@ -141,8 +120,7 @@ namespace Avalonia.Controls.UnitTests Content = child.Object, }; - target.ApplyTemplate(); - ((ContentPresenter)target.Presenter).UpdateChild(); + InitializeScrollViewer(target); Assert.Equal(new Size(45, 67), target.LargeChange); } @@ -154,8 +132,8 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); var raised = 0; - target.SetValue(ScrollViewer.ExtentProperty, new Size(100, 100)); - target.SetValue(ScrollViewer.ViewportProperty, new Size(50, 50)); + target.Extent = new Size(100, 100); + target.Viewport = new Size(50, 50); target.Offset = new Vector(10, 10); root.LayoutManager.ExecuteInitialLayoutPass(); @@ -168,7 +146,7 @@ namespace Avalonia.Controls.UnitTests ++raised; }; - target.SetValue(ScrollViewer.ExtentProperty, new Size(111, 112)); + target.Extent = new Size(111, 112); Assert.Equal(0, raised); @@ -184,8 +162,8 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); var raised = 0; - target.SetValue(ScrollViewer.ExtentProperty, new Size(100, 100)); - target.SetValue(ScrollViewer.ViewportProperty, new Size(50, 50)); + target.Extent = new Size(100, 100); + target.Viewport = new Size(50, 50); target.Offset = new Vector(10, 10); root.LayoutManager.ExecuteInitialLayoutPass(); @@ -214,8 +192,8 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); var raised = 0; - target.SetValue(ScrollViewer.ExtentProperty, new Size(100, 100)); - target.SetValue(ScrollViewer.ViewportProperty, new Size(50, 50)); + target.Extent = new Size(100, 100); + target.Viewport = new Size(50, 50); target.Offset = new Vector(10, 10); root.LayoutManager.ExecuteInitialLayoutPass(); @@ -228,7 +206,7 @@ namespace Avalonia.Controls.UnitTests ++raised; }; - target.SetValue(ScrollViewer.ViewportProperty, new Size(56, 58)); + target.Viewport = new Size(56, 58); Assert.Equal(0, raised); @@ -290,34 +268,32 @@ namespace Avalonia.Controls.UnitTests new ScrollContentPresenter { Name = "PART_ContentPresenter", - [~ContentPresenter.ContentProperty] = control[~ContentControl.ContentProperty], - [~~ScrollContentPresenter.ExtentProperty] = control[~~ScrollViewer.ExtentProperty], - [~~ScrollContentPresenter.OffsetProperty] = control[~~ScrollViewer.OffsetProperty], - [~~ScrollContentPresenter.ViewportProperty] = control[~~ScrollViewer.ViewportProperty], - [~ScrollContentPresenter.CanHorizontallyScrollProperty] = control[~ScrollViewer.CanHorizontallyScrollProperty], }.RegisterInNameScope(scope), new ScrollBar { - Name = "horizontalScrollBar", + Name = "PART_HorizontalScrollBar", Orientation = Orientation.Horizontal, - [~RangeBase.MaximumProperty] = control[~ScrollViewer.HorizontalScrollBarMaximumProperty], - [~~RangeBase.ValueProperty] = control[~~ScrollViewer.HorizontalScrollBarValueProperty], - [~ScrollBar.ViewportSizeProperty] = control[~ScrollViewer.HorizontalScrollBarViewportSizeProperty], [~ScrollBar.VisibilityProperty] = control[~ScrollViewer.HorizontalScrollBarVisibilityProperty], [Grid.RowProperty] = 1, }.RegisterInNameScope(scope), new ScrollBar { - Name = "verticalScrollBar", + Name = "PART_VerticalScrollBar", Orientation = Orientation.Vertical, - [~RangeBase.MaximumProperty] = control[~ScrollViewer.VerticalScrollBarMaximumProperty], - [~~RangeBase.ValueProperty] = control[~~ScrollViewer.VerticalScrollBarValueProperty], - [~ScrollBar.ViewportSizeProperty] = control[~ScrollViewer.VerticalScrollBarViewportSizeProperty], [~ScrollBar.VisibilityProperty] = control[~ScrollViewer.VerticalScrollBarVisibilityProperty], [Grid.ColumnProperty] = 1, }.RegisterInNameScope(scope), }, }; } + + private static void InitializeScrollViewer(ScrollViewer target) + { + target.ApplyTemplate(); + + var presenter = (ScrollContentPresenter)target.Presenter; + presenter.AttachToScrollViewer(); + presenter.UpdateChild(); + } } } diff --git a/tests/Avalonia.Controls.UnitTests/VirtualizingCarouselPanelTests.cs b/tests/Avalonia.Controls.UnitTests/VirtualizingCarouselPanelTests.cs index 721e8bde68..5e276b5911 100644 --- a/tests/Avalonia.Controls.UnitTests/VirtualizingCarouselPanelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/VirtualizingCarouselPanelTests.cs @@ -163,13 +163,13 @@ namespace Avalonia.Controls.UnitTests true, It.IsAny())) .Returns(() => transitionTask.Task); - + carousel.SelectedIndex = 1; Layout(target); Assert.Equal(items, target.Children); Assert.All(items, x => Assert.True(x.IsVisible)); - + transitionTask.SetResult(); sync.ExecutePostedCallbacks(); @@ -255,12 +255,6 @@ namespace Avalonia.Controls.UnitTests new ScrollContentPresenter { Name = "PART_ContentPresenter", - [~ScrollContentPresenter.ContentProperty] = parent.GetObservable(ScrollViewer.ContentProperty).ToBinding(), - [~~ScrollContentPresenter.ExtentProperty] = parent[~~ScrollViewer.ExtentProperty], - [~~ScrollContentPresenter.OffsetProperty] = parent[~~ScrollViewer.OffsetProperty], - [~~ScrollContentPresenter.ViewportProperty] = parent[~~ScrollViewer.ViewportProperty], - [~ScrollContentPresenter.CanHorizontallyScrollProperty] = parent[~ScrollViewer.CanHorizontallyScrollProperty], - [~ScrollContentPresenter.CanVerticallyScrollProperty] = parent[~ScrollViewer.CanVerticallyScrollProperty], }.RegisterInNameScope(scope), } }); diff --git a/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs b/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs index 7f5c0eb134..aa3d90b20f 100644 --- a/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs @@ -565,12 +565,6 @@ namespace Avalonia.Controls.UnitTests new ScrollContentPresenter { Name = "PART_ContentPresenter", - [~ContentPresenter.ContentProperty] = x[~ContentControl.ContentProperty], - [~~ScrollContentPresenter.ExtentProperty] = x[~~ScrollViewer.ExtentProperty], - [~~ScrollContentPresenter.OffsetProperty] = x[~~ScrollViewer.OffsetProperty], - [~~ScrollContentPresenter.ViewportProperty] = x[~~ScrollViewer.ViewportProperty], - [~ScrollContentPresenter.CanHorizontallyScrollProperty] = x[~ScrollViewer.CanHorizontallyScrollProperty], - [~ScrollContentPresenter.CanVerticallyScrollProperty] = x[~ScrollViewer.CanVerticallyScrollProperty], }.RegisterInNameScope(ns)); }