| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487 |
- // Copyright (c) The Avalonia Project. All rights reserved.
- // Licensed under the MIT license. See licence.md file in the project root for full license information.
- using System;
- using Avalonia.Controls.Presenters;
- using Avalonia.Controls.Primitives;
- using Avalonia.Input;
- namespace Avalonia.Controls
- {
- /// <summary>
- /// A control scrolls its content if the content is bigger than the space available.
- /// </summary>
- public class ScrollViewer : ContentControl, IScrollable, IScrollAnchorProvider
- {
- /// <summary>
- /// Defines the <see cref="CanHorizontallyScroll"/> property.
- /// </summary>
- /// <remarks>
- /// There is no public C# accessor for this property as it is intended to be bound to by a
- /// <see cref="ScrollContentPresenter"/> in the control's template.
- /// </remarks>
- public static readonly DirectProperty<ScrollViewer, bool> CanHorizontallyScrollProperty =
- AvaloniaProperty.RegisterDirect<ScrollViewer, bool>(
- nameof(CanHorizontallyScroll),
- o => o.CanHorizontallyScroll);
- /// <summary>
- /// Defines the <see cref="CanVerticallyScroll"/> property.
- /// </summary>
- /// <remarks>
- /// There is no public C# accessor for this property as it is intended to be bound to by a
- /// <see cref="ScrollContentPresenter"/> in the control's template.
- /// </remarks>
- public static readonly DirectProperty<ScrollViewer, bool> CanVerticallyScrollProperty =
- AvaloniaProperty.RegisterDirect<ScrollViewer, bool>(
- nameof(CanVerticallyScroll),
- o => o.CanVerticallyScroll);
- /// <summary>
- /// Defines the <see cref="Extent"/> property.
- /// </summary>
- public static readonly DirectProperty<ScrollViewer, Size> ExtentProperty =
- AvaloniaProperty.RegisterDirect<ScrollViewer, Size>(nameof(Extent),
- o => o.Extent,
- (o, v) => o.Extent = v);
- /// <summary>
- /// Defines the <see cref="Offset"/> property.
- /// </summary>
- public static readonly DirectProperty<ScrollViewer, Vector> OffsetProperty =
- AvaloniaProperty.RegisterDirect<ScrollViewer, Vector>(
- nameof(Offset),
- o => o.Offset,
- (o, v) => o.Offset = v);
- /// <summary>
- /// Defines the <see cref="Viewport"/> property.
- /// </summary>
- public static readonly DirectProperty<ScrollViewer, Size> ViewportProperty =
- AvaloniaProperty.RegisterDirect<ScrollViewer, Size>(nameof(Viewport),
- o => o.Viewport,
- (o, v) => o.Viewport = v);
- /// <summary>
- /// Defines the HorizontalScrollBarMaximum property.
- /// </summary>
- /// <remarks>
- /// There is no public C# accessor for this property as it is intended to be bound to by a
- /// <see cref="ScrollContentPresenter"/> in the control's template.
- /// </remarks>
- public static readonly DirectProperty<ScrollViewer, double> HorizontalScrollBarMaximumProperty =
- AvaloniaProperty.RegisterDirect<ScrollViewer, double>(
- nameof(HorizontalScrollBarMaximum),
- o => o.HorizontalScrollBarMaximum);
- /// <summary>
- /// Defines the HorizontalScrollBarValue property.
- /// </summary>
- /// <remarks>
- /// There is no public C# accessor for this property as it is intended to be bound to by a
- /// <see cref="ScrollContentPresenter"/> in the control's template.
- /// </remarks>
- public static readonly DirectProperty<ScrollViewer, double> HorizontalScrollBarValueProperty =
- AvaloniaProperty.RegisterDirect<ScrollViewer, double>(
- nameof(HorizontalScrollBarValue),
- o => o.HorizontalScrollBarValue,
- (o, v) => o.HorizontalScrollBarValue = v);
- /// <summary>
- /// Defines the HorizontalScrollBarViewportSize property.
- /// </summary>
- /// <remarks>
- /// There is no public C# accessor for this property as it is intended to be bound to by a
- /// <see cref="ScrollContentPresenter"/> in the control's template.
- /// </remarks>
- public static readonly DirectProperty<ScrollViewer, double> HorizontalScrollBarViewportSizeProperty =
- AvaloniaProperty.RegisterDirect<ScrollViewer, double>(
- nameof(HorizontalScrollBarViewportSize),
- o => o.HorizontalScrollBarViewportSize);
- /// <summary>
- /// Defines the <see cref="HorizontalScrollBarVisibility"/> property.
- /// </summary>
- public static readonly AttachedProperty<ScrollBarVisibility> HorizontalScrollBarVisibilityProperty =
- AvaloniaProperty.RegisterAttached<ScrollViewer, Control, ScrollBarVisibility>(
- nameof(HorizontalScrollBarVisibility),
- ScrollBarVisibility.Hidden);
- /// <summary>
- /// Defines the VerticalScrollBarMaximum property.
- /// </summary>
- /// <remarks>
- /// There is no public C# accessor for this property as it is intended to be bound to by a
- /// <see cref="ScrollContentPresenter"/> in the control's template.
- /// </remarks>
- public static readonly DirectProperty<ScrollViewer, double> VerticalScrollBarMaximumProperty =
- AvaloniaProperty.RegisterDirect<ScrollViewer, double>(
- nameof(VerticalScrollBarMaximum),
- o => o.VerticalScrollBarMaximum);
- /// <summary>
- /// Defines the VerticalScrollBarValue property.
- /// </summary>
- /// <remarks>
- /// There is no public C# accessor for this property as it is intended to be bound to by a
- /// <see cref="ScrollContentPresenter"/> in the control's template.
- /// </remarks>
- public static readonly DirectProperty<ScrollViewer, double> VerticalScrollBarValueProperty =
- AvaloniaProperty.RegisterDirect<ScrollViewer, double>(
- nameof(VerticalScrollBarValue),
- o => o.VerticalScrollBarValue,
- (o, v) => o.VerticalScrollBarValue = v);
- /// <summary>
- /// Defines the VerticalScrollBarViewportSize property.
- /// </summary>
- /// <remarks>
- /// There is no public C# accessor for this property as it is intended to be bound to by a
- /// <see cref="ScrollContentPresenter"/> in the control's template.
- /// </remarks>
- public static readonly DirectProperty<ScrollViewer, double> VerticalScrollBarViewportSizeProperty =
- AvaloniaProperty.RegisterDirect<ScrollViewer, double>(
- nameof(VerticalScrollBarViewportSize),
- o => o.VerticalScrollBarViewportSize);
- /// <summary>
- /// Defines the <see cref="VerticalScrollBarVisibility"/> property.
- /// </summary>
- public static readonly AttachedProperty<ScrollBarVisibility> VerticalScrollBarVisibilityProperty =
- AvaloniaProperty.RegisterAttached<ScrollViewer, Control, ScrollBarVisibility>(
- nameof(VerticalScrollBarVisibility),
- ScrollBarVisibility.Auto);
- private Size _extent;
- private Vector _offset;
- private Size _viewport;
- /// <summary>
- /// Initializes static members of the <see cref="ScrollViewer"/> class.
- /// </summary>
- static ScrollViewer()
- {
- HorizontalScrollBarVisibilityProperty.Changed.AddClassHandler<ScrollViewer>((x, e) => x.ScrollBarVisibilityChanged(e));
- VerticalScrollBarVisibilityProperty.Changed.AddClassHandler<ScrollViewer>((x, e) => x.ScrollBarVisibilityChanged(e));
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="ScrollViewer"/> class.
- /// </summary>
- public ScrollViewer()
- {
- }
- /// <summary>
- /// Gets the extent of the scrollable content.
- /// </summary>
- public Size Extent
- {
- get
- {
- return _extent;
- }
- private set
- {
- if (SetAndRaise(ExtentProperty, ref _extent, value))
- {
- CalculatedPropertiesChanged();
- }
- }
- }
- /// <summary>
- /// Gets or sets the current scroll offset.
- /// </summary>
- public Vector Offset
- {
- get
- {
- return _offset;
- }
- set
- {
- value = ValidateOffset(this, value);
- if (SetAndRaise(OffsetProperty, ref _offset, value))
- {
- CalculatedPropertiesChanged();
- }
- }
- }
- /// <summary>
- /// Gets the size of the viewport on the scrollable content.
- /// </summary>
- public Size Viewport
- {
- get
- {
- return _viewport;
- }
- private set
- {
- if (SetAndRaise(ViewportProperty, ref _viewport, value))
- {
- CalculatedPropertiesChanged();
- }
- }
- }
- /// <summary>
- /// Gets or sets the horizontal scrollbar visibility.
- /// </summary>
- public ScrollBarVisibility HorizontalScrollBarVisibility
- {
- get { return GetValue(HorizontalScrollBarVisibilityProperty); }
- set { SetValue(HorizontalScrollBarVisibilityProperty, value); }
- }
- /// <summary>
- /// Gets or sets the vertical scrollbar visibility.
- /// </summary>
- public ScrollBarVisibility VerticalScrollBarVisibility
- {
- get { return GetValue(VerticalScrollBarVisibilityProperty); }
- set { SetValue(VerticalScrollBarVisibilityProperty, value); }
- }
- /// <summary>
- /// Scrolls to the top-left corner of the content.
- /// </summary>
- public void ScrollToHome()
- {
- Offset = new Vector(double.NegativeInfinity, double.NegativeInfinity);
- }
- /// <summary>
- /// Scrolls to the bottom-left corner of the content.
- /// </summary>
- public void ScrollToEnd()
- {
- Offset = new Vector(double.NegativeInfinity, double.PositiveInfinity);
- }
- /// <summary>
- /// Gets a value indicating whether the viewer can scroll horizontally.
- /// </summary>
- protected bool CanHorizontallyScroll
- {
- get { return HorizontalScrollBarVisibility != ScrollBarVisibility.Disabled; }
- }
- /// <summary>
- /// Gets a value indicating whether the viewer can scroll vertically.
- /// </summary>
- protected bool CanVerticallyScroll
- {
- get { return VerticalScrollBarVisibility != ScrollBarVisibility.Disabled; }
- }
- /// <summary>
- /// Gets the maximum horizontal scrollbar value.
- /// </summary>
- protected double HorizontalScrollBarMaximum
- {
- get { return Max(_extent.Width - _viewport.Width, 0); }
- }
- /// <summary>
- /// Gets or sets the horizontal scrollbar value.
- /// </summary>
- protected double HorizontalScrollBarValue
- {
- get { return _offset.X; }
- set
- {
- if (_offset.X != value)
- {
- var old = Offset.X;
- Offset = Offset.WithX(value);
- RaisePropertyChanged(HorizontalScrollBarValueProperty, old, value);
- }
- }
- }
- /// <summary>
- /// Gets the size of the horizontal scrollbar viewport.
- /// </summary>
- protected double HorizontalScrollBarViewportSize
- {
- get { return _viewport.Width; }
- }
- /// <summary>
- /// Gets the maximum vertical scrollbar value.
- /// </summary>
- protected double VerticalScrollBarMaximum
- {
- get { return Max(_extent.Height - _viewport.Height, 0); }
- }
- /// <summary>
- /// Gets or sets the vertical scrollbar value.
- /// </summary>
- protected double VerticalScrollBarValue
- {
- get { return _offset.Y; }
- set
- {
- if (_offset.Y != value)
- {
- var old = Offset.Y;
- Offset = Offset.WithY(value);
- RaisePropertyChanged(VerticalScrollBarValueProperty, old, value);
- }
- }
- }
- /// <summary>
- /// Gets the size of the vertical scrollbar viewport.
- /// </summary>
- protected double VerticalScrollBarViewportSize
- {
- get { return _viewport.Height; }
- }
- /// <inheritdoc/>
- IControl IScrollAnchorProvider.CurrentAnchor => null; // TODO: Implement
- /// <summary>
- /// Gets the value of the HorizontalScrollBarVisibility attached property.
- /// </summary>
- /// <param name="control">The control to read the value from.</param>
- /// <returns>The value of the property.</returns>
- public static ScrollBarVisibility GetHorizontalScrollBarVisibility(Control control)
- {
- return control.GetValue(HorizontalScrollBarVisibilityProperty);
- }
- /// <summary>
- /// Gets the value of the HorizontalScrollBarVisibility attached property.
- /// </summary>
- /// <param name="control">The control to set the value on.</param>
- /// <param name="value">The value of the property.</param>
- public static void SetHorizontalScrollBarVisibility(Control control, ScrollBarVisibility value)
- {
- control.SetValue(HorizontalScrollBarVisibilityProperty, value);
- }
- /// <summary>
- /// Gets the value of the VerticalScrollBarVisibility attached property.
- /// </summary>
- /// <param name="control">The control to read the value from.</param>
- /// <returns>The value of the property.</returns>
- public static ScrollBarVisibility GetVerticalScrollBarVisibility(Control control)
- {
- return control.GetValue(VerticalScrollBarVisibilityProperty);
- }
- /// <summary>
- /// Gets the value of the VerticalScrollBarVisibility attached property.
- /// </summary>
- /// <param name="control">The control to set the value on.</param>
- /// <param name="value">The value of the property.</param>
- public static void SetVerticalScrollBarVisibility(Control control, ScrollBarVisibility value)
- {
- control.SetValue(VerticalScrollBarVisibilityProperty, value);
- }
- void IScrollAnchorProvider.RegisterAnchorCandidate(IControl element)
- {
- // TODO: Implement
- }
- void IScrollAnchorProvider.UnregisterAnchorCandidate(IControl element)
- {
- // TODO: Implement
- }
- internal static Vector CoerceOffset(Size extent, Size viewport, Vector offset)
- {
- 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));
- }
- private static double Clamp(double value, double min, double max)
- {
- return (value < min) ? min : (value > max) ? max : value;
- }
- private static double Max(double x, double y)
- {
- var result = Math.Max(x, y);
- return double.IsNaN(result) ? 0 : result;
- }
- private static Vector ValidateOffset(AvaloniaObject o, Vector value)
- {
- ScrollViewer scrollViewer = o as ScrollViewer;
- if (scrollViewer != null)
- {
- var extent = scrollViewer.Extent;
- var viewport = scrollViewer.Viewport;
- return CoerceOffset(extent, viewport, value);
- }
- else
- {
- return value;
- }
- }
- private void ScrollBarVisibilityChanged(AvaloniaPropertyChangedEventArgs e)
- {
- var wasEnabled = !ScrollBarVisibility.Disabled.Equals(e.OldValue);
- var isEnabled = !ScrollBarVisibility.Disabled.Equals(e.NewValue);
- 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 properies.
- RaisePropertyChanged(HorizontalScrollBarMaximumProperty, 0, HorizontalScrollBarMaximum);
- RaisePropertyChanged(HorizontalScrollBarValueProperty, 0, HorizontalScrollBarValue);
- RaisePropertyChanged(HorizontalScrollBarViewportSizeProperty, 0, HorizontalScrollBarViewportSize);
- RaisePropertyChanged(VerticalScrollBarMaximumProperty, 0, VerticalScrollBarMaximum);
- RaisePropertyChanged(VerticalScrollBarValueProperty, 0, VerticalScrollBarValue);
- RaisePropertyChanged(VerticalScrollBarViewportSizeProperty, 0, VerticalScrollBarViewportSize);
- }
- protected override void OnKeyDown(KeyEventArgs e)
- {
- if (e.Key == Key.PageUp)
- {
- VerticalScrollBarValue = Math.Max(_offset.Y - _viewport.Height, 0);
- e.Handled = true;
- }
- else if (e.Key == Key.PageDown)
- {
- VerticalScrollBarValue = Math.Min(_offset.Y + _viewport.Height, VerticalScrollBarMaximum);
- e.Handled = true;
- }
- }
- }
- }
|