I would like to create the following behaviour in a ScrollViewer
that wraps ContentControl
:
When the ContentControl
height grows , the ScrollViewer
should automatically scroll to the end. This is easy to achive by using ScrollViewer.ScrollToEnd()
.
However, if the user uses the scroll bar, the automatic scrolling shouldn't happen anymore. This is similar to what happens in VS output window for example.
The problem is to know when a scrolling has happened because of user scrolling and when it happened because the content size changed. I tried to play with the ScrollChangedEventArgs
of ScrollChangedEvent
, but couldn't get it to work.
Ideally, I do not want to handle all possible Mouse and keyboard events.
To use you just need to press CTRL+ Left click of your mouse and drag the mouse a bit in the direction you want to scroll the page. For example, if you want to scroll up to the page automatically, click CTRL+ left click and slightly move your mouse upwards, the tool will start scrolling up the page.
Set the overflow-x:hidden; and overflow-y:auto; that will automatically hide the horizontal scroll bar and present only vertical scrollbar. Here the scroll div will be vertically scrollable.
The first one is with javascript: set the scrollTop property of the scrollable element (e.g. document. body. scrollTop = 1000; ). The second is setting the link to point to a specific id in the page e.g.
You can use ScrollChangedEventArgs.ExtentHeightChange to know if a ScrollChanged is due to a change in the content or to a user action... When the content is unchanged, the ScrollBar position sets or unsets the auto-scroll mode. When the content has changed you can apply auto-scrolling.
Code behind:
private Boolean AutoScroll = true; private void ScrollViewer_ScrollChanged(Object sender, ScrollChangedEventArgs e) { // User scroll event : set or unset auto-scroll mode if (e.ExtentHeightChange == 0) { // Content unchanged : user scroll event if (ScrollViewer.VerticalOffset == ScrollViewer.ScrollableHeight) { // Scroll bar is in bottom // Set auto-scroll mode AutoScroll = true; } else { // Scroll bar isn't in bottom // Unset auto-scroll mode AutoScroll = false; } } // Content scroll event : auto-scroll eventually if (AutoScroll && e.ExtentHeightChange != 0) { // Content changed and auto-scroll mode set // Autoscroll ScrollViewer.ScrollToVerticalOffset(ScrollViewer.ExtentHeight); } }
Here is an adaptation from several sources.
public class ScrollViewerExtensions { public static readonly DependencyProperty AlwaysScrollToEndProperty = DependencyProperty.RegisterAttached("AlwaysScrollToEnd", typeof(bool), typeof(ScrollViewerExtensions), new PropertyMetadata(false, AlwaysScrollToEndChanged)); private static bool _autoScroll; private static void AlwaysScrollToEndChanged(object sender, DependencyPropertyChangedEventArgs e) { ScrollViewer scroll = sender as ScrollViewer; if (scroll != null) { bool alwaysScrollToEnd = (e.NewValue != null) && (bool)e.NewValue; if (alwaysScrollToEnd) { scroll.ScrollToEnd(); scroll.ScrollChanged += ScrollChanged; } else { scroll.ScrollChanged -= ScrollChanged; } } else { throw new InvalidOperationException("The attached AlwaysScrollToEnd property can only be applied to ScrollViewer instances."); } } public static bool GetAlwaysScrollToEnd(ScrollViewer scroll) { if (scroll == null) { throw new ArgumentNullException("scroll"); } return (bool)scroll.GetValue(AlwaysScrollToEndProperty); } public static void SetAlwaysScrollToEnd(ScrollViewer scroll, bool alwaysScrollToEnd) { if (scroll == null) { throw new ArgumentNullException("scroll"); } scroll.SetValue(AlwaysScrollToEndProperty, alwaysScrollToEnd); } private static void ScrollChanged(object sender, ScrollChangedEventArgs e) { ScrollViewer scroll = sender as ScrollViewer; if (scroll == null) { throw new InvalidOperationException("The attached AlwaysScrollToEnd property can only be applied to ScrollViewer instances."); } // User scroll event : set or unset autoscroll mode if (e.ExtentHeightChange == 0) { _autoScroll = scroll.VerticalOffset == scroll.ScrollableHeight; } // Content scroll event : autoscroll eventually if (_autoScroll && e.ExtentHeightChange != 0) { scroll.ScrollToVerticalOffset(scroll.ExtentHeight); } } }
Use it in your XAML like so:
<ScrollViewer Height="230" HorizontalScrollBarVisibility="Auto" extensionProperties:ScrollViewerExtension.AlwaysScrollToEnd="True"> <TextBlock x:Name="Trace"/> </ScrollViewer>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With