On my side, I kept the minimum finger length because the touch inputs require a minimum thumb size to optimize touch.
You can define a ScrollViewer ControlTemplate that will use the TouchScrollBar as its horizontal and vertical ScrollBar.
See the UpdateViewPort method for math.
Sorry, I do not see a use case for explicitly setting the thumb of the scroll bar to cover a percentage of the track length
public class TouchScrollBar : System.Windows.Controls.Primitives.ScrollBar { #region Fields #region Dependency properties public static readonly DependencyProperty MinThumbLengthProperty = DependencyProperty.Register ("MinThumbLength", typeof(double), typeof(TouchScrollBar), new UIPropertyMetadata((double)0, OnMinThumbLengthPropertyChanged)); #endregion private double? m_originalViewportSize; #endregion #region Properties public double MinThumbLength { get { return (double)GetValue(MinThumbLengthProperty); } set { SetValue(MinThumbLengthProperty, value); } } #endregion #region Constructors public TouchScrollBar() { SizeChanged += OnSizeChanged; } private bool m_trackSubscribed; void OnSizeChanged(object sender, SizeChangedEventArgs e) { SubscribeTrack(); } private void SubscribeTrack() { if (!m_trackSubscribed && Track != null) { Track.SizeChanged += OnTrackSizeChanged; m_trackSubscribed = true; } } #endregion #region Protected and private methods #region Event handlers #region Dependency properties event handlers private void OnMinThumbLengthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TouchScrollBar instance = d as TouchScrollBar; if(instance != null) { instance.OnMinThumbLengthChanged(e); } } #endregion protected void OnTrackSizeChanged(object sender, SizeChangedEventArgs e) { SubscribeTrack(); UpdateViewPort(); } protected override void OnMaximumChanged(double oldMaximum, double newMaximum) { base.OnMaximumChanged(oldMaximum, newMaximum); SubscribeTrack(); UpdateViewPort(); } protected override void OnMinimumChanged(double oldMinimum, double newMinimum) { base.OnMinimumChanged(oldMinimum, newMinimum); SubscribeTrack(); UpdateViewPort(); } protected void OnMinThumbLengthChanged(DependencyPropertyChangedEventArgs e) { SubscribeTrack(); UpdateViewPort(); } #endregion private void UpdateViewPort() { if(Track != null) { if(m_originalViewportSize == null) { m_originalViewportSize = ViewportSize; } double trackLength = Orientation == Orientation.Vertical ? Track.ActualHeight : Track.ActualWidth; double thumbHeight = m_originalViewportSize.Value / (Maximum - Minimum + m_originalViewportSize.Value) * trackLength; if (thumbHeight < MinThumbLength && !double.IsNaN(thumbHeight)) { ViewportSize = (MinThumbLength * (Maximum - Minimum)) / (trackLength + MinThumbLength); } } } #endregion }
}
Nadzzz
source share