Browse Source

Merge pull request #3729 from AvaloniaUI/feature/scrollchanged

Implement ScrollViewer.ScrollChanged
pull/3800/head
Steven Kirk 6 years ago
committed by GitHub
parent
commit
381815efa0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 45
      src/Avalonia.Controls/ScrollChangedEventArgs.cs
  2. 49
      src/Avalonia.Controls/ScrollViewer.cs
  3. 73
      tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs

45
src/Avalonia.Controls/ScrollChangedEventArgs.cs

@ -0,0 +1,45 @@
using Avalonia.Interactivity;
namespace Avalonia.Controls
{
/// <summary>
/// Describes a change in scrolling state.
/// </summary>
public class ScrollChangedEventArgs : RoutedEventArgs
{
public ScrollChangedEventArgs(
Vector extentDelta,
Vector offsetDelta,
Vector viewportDelta)
: this(ScrollViewer.ScrollChangedEvent, extentDelta, offsetDelta, viewportDelta)
{
}
public ScrollChangedEventArgs(
RoutedEvent routedEvent,
Vector extentDelta,
Vector offsetDelta,
Vector viewportDelta)
: base(routedEvent)
{
ExtentDelta = extentDelta;
OffsetDelta = offsetDelta;
ViewportDelta = viewportDelta;
}
/// <summary>
/// Gets the change to the value of <see cref="ScrollViewer.Extent"/>.
/// </summary>
public Vector ExtentDelta { get; }
/// <summary>
/// Gets the change to the value of <see cref="ScrollViewer.Offset"/>.
/// </summary>
public Vector OffsetDelta { get; }
/// <summary>
/// Gets the change to the value of <see cref="ScrollViewer.Viewport"/>.
/// </summary>
public Vector ViewportDelta { get; }
}
}

49
src/Avalonia.Controls/ScrollViewer.cs

@ -2,6 +2,7 @@ using System;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
namespace Avalonia.Controls
{
@ -165,6 +166,14 @@ namespace Avalonia.Controls
nameof(VerticalScrollBarVisibility),
ScrollBarVisibility.Auto);
/// <summary>
/// Defines the <see cref="ScrollChanged"/> event.
/// </summary>
public static readonly RoutedEvent<ScrollChangedEventArgs> ScrollChangedEvent =
RoutedEvent.Register<ScrollViewer, ScrollChangedEventArgs>(
nameof(ScrollChanged),
RoutingStrategies.Bubble);
internal const double DefaultSmallChange = 16;
private IDisposable _childSubscription;
@ -191,6 +200,15 @@ namespace Avalonia.Controls
{
}
/// <summary>
/// Occurs when changes are detected to the scroll position, extent, or viewport size.
/// </summary>
public event EventHandler<ScrollChangedEventArgs> ScrollChanged
{
add => AddHandler(ScrollChangedEvent, value);
remove => RemoveHandler(ScrollChangedEvent, value);
}
/// <summary>
/// Gets the extent of the scrollable content.
/// </summary>
@ -203,9 +221,11 @@ namespace Avalonia.Controls
private set
{
var old = _extent;
if (SetAndRaise(ExtentProperty, ref _extent, value))
{
CalculatedPropertiesChanged();
CalculatedPropertiesChanged(extentDelta: value - old);
}
}
}
@ -222,11 +242,13 @@ namespace Avalonia.Controls
set
{
var old = _offset;
value = ValidateOffset(this, value);
if (SetAndRaise(OffsetProperty, ref _offset, value))
{
CalculatedPropertiesChanged();
CalculatedPropertiesChanged(offsetDelta: value - old);
}
}
}
@ -243,9 +265,11 @@ namespace Avalonia.Controls
private set
{
var old = _viewport;
if (SetAndRaise(ViewportProperty, ref _viewport, value))
{
CalculatedPropertiesChanged();
CalculatedPropertiesChanged(viewportDelta: value - old);
}
}
}
@ -525,7 +549,10 @@ namespace Avalonia.Controls
}
}
private void CalculatedPropertiesChanged()
private void CalculatedPropertiesChanged(
Size extentDelta = default,
Vector offsetDelta = default,
Size viewportDelta = default)
{
// 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.
@ -546,6 +573,20 @@ namespace Avalonia.Controls
SetAndRaise(SmallChangeProperty, ref _smallChange, new Size(DefaultSmallChange, DefaultSmallChange));
SetAndRaise(LargeChangeProperty, ref _largeChange, Viewport);
}
if (extentDelta != default || offsetDelta != default || viewportDelta != default)
{
using var route = BuildEventRoute(ScrollChangedEvent);
if (route.HasHandlers)
{
var e = new ScrollChangedEventArgs(
new Vector(extentDelta.Width, extentDelta.Height),
offsetDelta,
new Vector(viewportDelta.Width, viewportDelta.Height));
route.RaiseEvent(this, e);
}
}
}
protected override void OnKeyDown(KeyEventArgs e)

73
tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs

@ -4,7 +4,6 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Moq;
using Xunit;
@ -147,6 +146,78 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new Size(45, 67), target.LargeChange);
}
[Fact]
public void Changing_Extent_Should_Raise_ScrollChanged()
{
var target = new ScrollViewer();
var raised = 0;
target.SetValue(ScrollViewer.ExtentProperty, new Size(100, 100));
target.SetValue(ScrollViewer.ViewportProperty, new Size(50, 50));
target.Offset = new Vector(10, 10);
target.ScrollChanged += (s, e) =>
{
Assert.Equal(new Vector(11, 12), e.ExtentDelta);
Assert.Equal(default, e.OffsetDelta);
Assert.Equal(default, e.ViewportDelta);
++raised;
};
target.SetValue(ScrollViewer.ExtentProperty, new Size(111, 112));
Assert.Equal(1, raised);
}
[Fact]
public void Changing_Offset_Should_Raise_ScrollChanged()
{
var target = new ScrollViewer();
var raised = 0;
target.SetValue(ScrollViewer.ExtentProperty, new Size(100, 100));
target.SetValue(ScrollViewer.ViewportProperty, new Size(50, 50));
target.Offset = new Vector(10, 10);
target.ScrollChanged += (s, e) =>
{
Assert.Equal(default, e.ExtentDelta);
Assert.Equal(new Vector(12, 14), e.OffsetDelta);
Assert.Equal(default, e.ViewportDelta);
++raised;
};
target.Offset = new Vector(22, 24);
Assert.Equal(1, raised);
}
[Fact]
public void Changing_Viewport_Should_Raise_ScrollChanged()
{
var target = new ScrollViewer();
var raised = 0;
target.SetValue(ScrollViewer.ExtentProperty, new Size(100, 100));
target.SetValue(ScrollViewer.ViewportProperty, new Size(50, 50));
target.Offset = new Vector(10, 10);
target.ScrollChanged += (s, e) =>
{
Assert.Equal(default, e.ExtentDelta);
Assert.Equal(default, e.OffsetDelta);
Assert.Equal(new Vector(6, 8), e.ViewportDelta);
++raised;
};
target.SetValue(ScrollViewer.ViewportProperty, new Size(56, 58));
Assert.Equal(1, raised);
}
private Control CreateTemplate(ScrollViewer control, INameScope scope)
{
return new Grid

Loading…
Cancel
Save