From 87e2ed810794d6d3ea72cb2b75d744adb278b0ab Mon Sep 17 00:00:00 2001 From: Tom Edwards Date: Sat, 1 Apr 2023 13:00:52 +0200 Subject: [PATCH] Don't detach from ScrollViewer when detached from visual tree Fixes offset resetting when switching between tabs --- .../Presenters/ScrollContentPresenter.cs | 27 ++++++++--------- src/Avalonia.Controls/Primitives/ScrollBar.cs | 29 +++++++++++-------- src/Avalonia.Controls/ScrollViewer.cs | 7 +++-- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index 6552ee6dbd..0efa8d38f8 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -304,15 +304,23 @@ namespace Avalonia.Controls.Presenters /// protected internal virtual void AttachToScrollViewer() { - _ownerSubscriptions?.Dispose(); - - var owner = _owner = this.FindAncestorOfType(); + var owner = this.FindAncestorOfType(); if (owner == null) { + _owner = null; + _ownerSubscriptions?.Dispose(); + _ownerSubscriptions = null; return; } + if (owner == _owner) + { + return; + } + + _ownerSubscriptions?.Dispose(); + var subscriptionDisposables = new IDisposable?[] { IfUnset(CanHorizontallyScrollProperty, p => Bind(p, owner.GetObservable(ScrollViewer.HorizontalScrollBarVisibilityProperty, NotDisabled), Data.BindingPriority.Template)), @@ -322,19 +330,12 @@ namespace Avalonia.Controls.Presenters IfUnset(ContentProperty, p => Bind(p, owner.GetBindingObservable(ContentProperty), Data.BindingPriority.Template)), }.Where(d => d != null).Cast().ToArray(); + _owner = owner; _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(); - _owner = null; - base.OnDetachedFromVisualTree(e); + IDisposable? IfUnset(T property, Func func) where T : AvaloniaProperty => IsSet(property) ? null : func(property); } /// @@ -708,7 +709,7 @@ namespace Avalonia.Controls.Presenters } CoerceValue(OffsetProperty); } - else if (change.Property == ViewportProperty && _owner != null) + else if (change.Property == ViewportProperty) { if (_owner != null) { diff --git a/src/Avalonia.Controls/Primitives/ScrollBar.cs b/src/Avalonia.Controls/Primitives/ScrollBar.cs index 29775f4526..37aa1ebffd 100644 --- a/src/Avalonia.Controls/Primitives/ScrollBar.cs +++ b/src/Avalonia.Controls/Primitives/ScrollBar.cs @@ -202,15 +202,23 @@ namespace Avalonia.Controls.Primitives /// protected internal virtual void AttachToScrollViewer() { - _ownerSubscriptions?.Dispose(); - - var owner = _owner = this.FindAncestorOfType(); + var owner = this.FindAncestorOfType(); if (owner == null) { + _owner = null; + _ownerSubscriptions?.Dispose(); + _ownerSubscriptions = null; return; } + if (owner == _owner) + { + return; + } + + _ownerSubscriptions?.Dispose(); + var visibilitySource = Orientation == Orientation.Horizontal ? ScrollViewer.HorizontalScrollBarVisibilityProperty : ScrollViewer.VerticalScrollBarVisibilityProperty; var subscriptionDisposables = new IDisposable?[] @@ -224,21 +232,15 @@ namespace Avalonia.Controls.Primitives IfUnset(SmallChangeProperty, p => Bind(p, owner.GetObservable(ScrollViewer.SmallChangeProperty).Select(ExtractOrdinate), BindingPriority.Template)) }.Where(d => d != null).Cast().ToArray(); + _owner = owner; _ownerSubscriptions = new CompositeDisposable(subscriptionDisposables); - IDisposable? IfUnset(T property, Func func) where T : AvaloniaProperty => GetValueStore().IsSet(property) ? null : func(property); + IDisposable? IfUnset(T property, Func func) where T : AvaloniaProperty => 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; - protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) - { - _ownerSubscriptions?.Dispose(); - _owner = null; - base.OnDetachedFromVisualTree(e); - } - protected override void OnKeyDown(KeyEventArgs e) { if (e.Key == Key.PageUp) @@ -260,7 +262,10 @@ namespace Avalonia.Controls.Primitives if (change.Property == OrientationProperty) { UpdatePseudoClasses(change.GetNewValue()); - AttachToScrollViewer(); // there's no way to manually refresh bindings, so reapply them + if (IsAttachedToVisualTree) + { + AttachToScrollViewer(); // there's no way to manually refresh bindings, so reapply them + } } else if (change.Property == AllowAutoHideProperty) { diff --git a/src/Avalonia.Controls/ScrollViewer.cs b/src/Avalonia.Controls/ScrollViewer.cs index 26b2a030c5..a7188c6226 100644 --- a/src/Avalonia.Controls/ScrollViewer.cs +++ b/src/Avalonia.Controls/ScrollViewer.cs @@ -660,8 +660,11 @@ namespace Avalonia.Controls private void CalculatedPropertiesChanged() { var newMaximum = ScrollBarMaximum; - RaisePropertyChanged(ScrollBarMaximumProperty, _oldMaximum, newMaximum); - _oldMaximum = newMaximum; + if (newMaximum != _oldMaximum) + { + RaisePropertyChanged(ScrollBarMaximumProperty, _oldMaximum, newMaximum); + _oldMaximum = newMaximum; + } if (_logicalScrollable?.IsLogicalScrollEnabled == true) {