First, take a look at the effect
Second, the principle
Although the effect is very simple, but some of the online information about the code is very impressive, and the effect is not very ideal, rolling without a smooth feeling. I provide here the source code altogether more than 120 lines, will be able to achieve the effect.
Essentially we just take over ScrollViewer's rolling logic and replace this logic with inertia, so how do we take over? The key here is to first block the ScrollViewer mouse wheel event:
1 protected Override void OnMouseWheel (MouseWheelEventArgs e) 2 {3 true ; 4 }
In this way, ScrollViewer will not respond to the wheel event, we are here to fuss. First we add an attribute Isenableinertia to this scrollviewer to control whether inertia is used, because the turnip greens each their own, do not want to force everyone to use inertia, so the wheel response method becomes:
1 protected Override voidOnMouseWheel (MouseWheelEventArgs e)2 {3 if(!Isenableinertia)4 {5 Base. OnMouseWheel (e);6 return;7 }8e.handled =true;9}
Control ScrollViewer Vertical Scrolling can be done using Scrollviewer.scrolltoverticaloffset , as well as horizontal. Why can't I use verticaloffset ? Because verticaloffset is read-only at the time of registration:
1 private static readonly DependencyPropertyKey Verticaloffsetpropertykey = dependencyproperty.registerreadonly (nameof (VerticalOffset), Span style= "COLOR: #0000ff" >typeof (double ), typeof (ScrollViewer), (PropertyMetadata) new frameworkpropertymetadata ((object ) 0.0 2 3 public static readonly DependencyProperty verticaloffsetproperty = ScrollViewer.VerticalOffsetPropertyKey.DependencyProperty;
Well, the next step is how to implement the inertial motion in the wheel response method, which is a deceleration motion. Think of here, the familiar animation of Bo friends soon know to use WPF animation to achieve, the default animation is a linear, to have inertia effect on the easing function, WPF has a lot of easing function, and cubicease is very suitable for inertia, and its description is as follows:
In the figure, the horizontal axis represents the time, and the longitudinal axes represent the distance. It's obvious that the middleEaseOutThe pattern is what we want. When we get here, the idea is clear, and we can define a property.Currentverticaloffset, we animate it above it and call it in its value callback function.Scrollviewer.scrolltoverticaloffsetTo update the scroll position of the ScrollViewer. Of course we also need a private field_totalverticaloffset, this is used to store ScrollViewer scrolling target position, scroll down one unit and we'll subtract it once.E.delta, where E is the parameter passed in by the wheel response method, and each time it is assigned a value, it can beCurrentverticaloffsetTo perform the animation on:beginanimation (Currentverticaloffsetproperty, animation), it is important to note that when a dependency property is animated, the assignment does not take effect, because when an animation reaches the end of the active period, the timeline defaults to its progress until the active and retention periods of its parent have ended. If you want to manually change the value of a dependency property after the animation has ended, you need toFillbehaviorSet to stop. However, there is a problem, once the animation is finished, this dependency property will restore the initial value, so also to the animation to subscribe to acompletedevent, in the event response method, for theCurrentverticaloffsetGiven the target value, i.e._totalverticaloffset。
Finally there is a conflict problem, when manually dragging the slider or when changing the scrollbar position with the context menu is not animated, because there is no triggerOnMouseWheelThat's okay, that's exactly what we want, but if it's triggered againOnMouseWheelThere's a problem, because when we manually trigger the scrolling we didn't giveCurrentverticaloffsetAnd_totalverticaloffsetAssign Value (CurrentverticaloffsetAnd_totalverticaloffsetOnly inOnMouseWheel), so before using the animation to perform the scrolling operation to determine whether it is necessary to update them both, how to judge? We can use a private field_isrunningTo maintain the state, assign a value of true at the beginning of the animation, and then assign false to the end. This way, when_isrunning = false , the description is called when theOnMouseWheelBefore, the animation has ended, the user may have manually changed the scrollbar position (or maybe not, but this does not affect), so it is necessary to update the two previous brothers value.
Because of the common inertia scrolling in the vertical direction mostly, so I did not write the horizontal direction of the logic, but also very easy to expand, interested bloggers can download the source code of their own research.
Third, the source code
The control source discussed in this article has been open source on GitHub: Github.com/nabian/handycontrol