diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs
index 95ee73be4e..5d71a499e3 100644
--- a/src/Avalonia.Controls.DataGrid/DataGrid.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs
@@ -2215,7 +2215,14 @@ namespace Avalonia.Controls
/// PointerWheelEventArgs
protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
{
- e.Handled = e.Handled || UpdateScroll(e.Delta * DATAGRID_mouseWheelDelta);
+ if(UpdateScroll(e.Delta * DATAGRID_mouseWheelDelta))
+ {
+ e.Handled = true;
+ }
+ else
+ {
+ e.Handled = e.Handled || !ScrollViewer.GetIsScrollChainingEnabled(this);
+ }
}
internal bool UpdateScroll(Vector delta)
diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
index a62ba306ab..8c5e644851 100644
--- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
@@ -60,6 +60,12 @@ namespace Avalonia.Controls.Presenters
o => o.Viewport,
(o, v) => o.Viewport = v);
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty IsScrollChainingEnabledProperty =
+ ScrollViewer.IsScrollChainingEnabledProperty.AddOwner();
+
private bool _canHorizontallyScroll;
private bool _canVerticallyScroll;
private bool _arranging;
@@ -138,6 +144,20 @@ namespace Avalonia.Controls.Presenters
private set { SetAndRaise(ViewportProperty, ref _viewport, value); }
}
+ ///
+ /// Gets or sets if scroll chaining is enabled. The default value is true.
+ ///
+ ///
+ /// After a user hits a scroll limit on an element that has been nested within another scrollable element,
+ /// you can specify whether that parent element should continue the scrolling operation begun in its child element.
+ /// This is called scroll chaining.
+ ///
+ public bool IsScrollChainingEnabled
+ {
+ get => GetValue(IsScrollChainingEnabledProperty);
+ set => SetValue(IsScrollChainingEnabledProperty, value);
+ }
+
///
IControl? IScrollAnchorProvider.CurrentAnchor
{
@@ -405,8 +425,11 @@ namespace Avalonia.Controls.Presenters
_activeLogicalGestureScrolls[e.Id] = delta;
}
- Offset = new Vector(x, y);
- e.Handled = true;
+ Vector newOffset = new Vector(x, y);
+ bool offsetChanged = newOffset != Offset;
+ Offset = newOffset;
+
+ e.Handled = !IsScrollChainingEnabled || offsetChanged;
}
}
@@ -440,8 +463,11 @@ namespace Avalonia.Controls.Presenters
x = Math.Min(x, Extent.Width - Viewport.Width);
}
- Offset = new Vector(x, y);
- e.Handled = true;
+ Vector newOffset = new Vector(x, y);
+ bool offsetChanged = newOffset != Offset;
+ Offset = newOffset;
+
+ e.Handled = !IsScrollChainingEnabled || offsetChanged;
}
}
diff --git a/src/Avalonia.Controls/ScrollViewer.cs b/src/Avalonia.Controls/ScrollViewer.cs
index eee6216587..10a014e81d 100644
--- a/src/Avalonia.Controls/ScrollViewer.cs
+++ b/src/Avalonia.Controls/ScrollViewer.cs
@@ -181,6 +181,14 @@ namespace Avalonia.Controls
nameof(AllowAutoHide),
true);
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AttachedProperty IsScrollChainingEnabledProperty =
+ AvaloniaProperty.RegisterAttached(
+ nameof(IsScrollChainingEnabled),
+ defaultValue: true);
+
///
/// Defines the event.
///
@@ -418,6 +426,20 @@ namespace Avalonia.Controls
set => SetValue(AllowAutoHideProperty, value);
}
+ ///
+ /// Gets or sets if scroll chaining is enabled. The default value is true.
+ ///
+ ///
+ /// After a user hits a scroll limit on an element that has been nested within another scrollable element,
+ /// you can specify whether that parent element should continue the scrolling operation begun in its child element.
+ /// This is called scroll chaining.
+ ///
+ public bool IsScrollChainingEnabled
+ {
+ get => GetValue(IsScrollChainingEnabledProperty);
+ set => SetValue(IsScrollChainingEnabledProperty, value);
+ }
+
///
/// Scrolls the content up one line.
///
@@ -548,6 +570,36 @@ namespace Avalonia.Controls
return control.GetValue(AllowAutoHideProperty);
}
+ ///
+ /// Sets the value of the IsScrollChainingEnabled attached property.
+ ///
+ /// The control to set the value on.
+ /// The value of the property.
+ ///
+ /// After a user hits a scroll limit on an element that has been nested within another scrollable element,
+ /// you can specify whether that parent element should continue the scrolling operation begun in its child element.
+ /// This is called scroll chaining.
+ ///
+ public static void SetIsScrollChainingEnabled(Control control, bool value)
+ {
+ control.SetValue(IsScrollChainingEnabledProperty, value);
+ }
+
+ ///
+ /// Gets the value of the IsScrollChainingEnabled attached property.
+ ///
+ /// The control to read the value from.
+ /// The value of the property.
+ ///
+ /// After a user hits a scroll limit on an element that has been nested within another scrollable element,
+ /// you can specify whether that parent element should continue the scrolling operation begun in its child element.
+ /// This is called scroll chaining.
+ ///
+ public static bool GetIsScrollChainingEnabled(Control control)
+ {
+ return control.GetValue(IsScrollChainingEnabledProperty);
+ }
+
///
/// Gets the value of the VerticalScrollBarVisibility attached property.
///
diff --git a/src/Avalonia.Themes.Default/Controls/ListBox.xaml b/src/Avalonia.Themes.Default/Controls/ListBox.xaml
index e3417aa086..b1fcb830b3 100644
--- a/src/Avalonia.Themes.Default/Controls/ListBox.xaml
+++ b/src/Avalonia.Themes.Default/Controls/ListBox.xaml
@@ -6,6 +6,7 @@
+
+ Viewport="{TemplateBinding Viewport, Mode=TwoWay}"
+ IsScrollChainingEnabled="{TemplateBinding IsScrollChainingEnabled}">
+
+
+
@@ -28,6 +29,7 @@
+ Viewport="{TemplateBinding Viewport, Mode=TwoWay}"
+ IsScrollChainingEnabled="{TemplateBinding IsScrollChainingEnabled}">
+
@@ -68,6 +69,7 @@
DockPanel.Dock="Top" />
+