1. Introduction
In WPF development, ScrollViewer is a commonly used control. In a project I worked on, we received feedback that when the content inside ScrollViewer is too long, the scrollbar's thumb becomes very small, making it difficult to click. Initially, I tried setting a minimum size for the thumb in the style, but it didn't work. Finally, I took a different approach: hide the original thumb and use a custom control as a replacement to indirectly control the scrolling of ScrollViewer.
2. Main Content
Here I will directly use the curve chart control I previously wrote for demonstration. When there is a large amount of curve data, the thumb appears very small. The following shows the default style; it would be even smaller under a custom style.

Here, I placed a
Canvason top of the curve chart and added aBorderto act as the thumb. Note that the entireCanvasis overlaid on the curve chart because I also added the functionality to drag and move the chart by clicking. Then, I hide theScrollViewer’s thumb, set the minimum width and height for the custom thumb, and hide it by default.<Grid> <local:CruveDrawingVisual x:Name="curve" Margin="0,10,0,15" /> <ScrollViewer Name="scroll" HorizontalScrollBarVisibility="Hidden" ScrollChanged="ScrollViewer_ScrollChanged" VerticalScrollBarVisibility="Disabled"> <Canvas x:Name="canvas" /> </ScrollViewer> <Canvas x:Name="CurvePanel" Background="Transparent"> <Border x:Name="border" Canvas.Left="0" Canvas.Bottom="0" Height="15" MinWidth="80" Background="Green" PreviewMouseLeftButtonDown="Border_PreviewMouseLeftButtonDown" Visibility="Collapsed" /> </Canvas> </Grid>Next, add the corresponding logic code in the code-behind. Detailed explanations are already included as comments in the code, so I won't elaborate further here.
public partial class MainWindow : Window { private bool isAdd = true; private List<int> lists = new List<int>(); private Point point_border; private double offset = -1; public MainWindow() { InitializeComponent(); CurvePanel.MouseMove += delegate (object sender, MouseEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed) { if (Mouse.Captured == null) Mouse.Capture(CurvePanel); // Drag the thumb if (isBorder) { Point point = e.GetPosition(this); // Mouse is beyond the left edge of the control if (point.X - point_border.X <= 0) { scroll.ScrollToHorizontalOffset(0); } // Mouse is beyond the right edge of the control else if (point.X - point_border.X >= CurvePanel.ActualWidth - border.ActualWidth) { scroll.ScrollToHorizontalOffset(lists.Count - CurvePanel.ActualWidth); } // Mouse is within the control area else if (point.X - point_border.X > 0 && point.X - point_border.X < CurvePanel.ActualWidth - border.ActualWidth) { double left = point.X - point_border.X; scroll.ScrollToHorizontalOffset((lists.Count - CurvePanel.ActualWidth) / (CurvePanel.ActualWidth - border.ActualWidth) * left); } } // Drag the canvas else { if (offset >= 0 && offset <= CurvePanel.ActualWidth) { scroll.ScrollToHorizontalOffset(scroll.HorizontalOffset - (e.GetPosition(this).X - offset)); } offset = e.GetPosition(this).X; } } else { offset = -1; isBorder = false; Mouse.Capture(null); // Release mouse capture } }; } private void Window_Loaded(object sender, RoutedEventArgs e) { int temp = 20; for (int i = 0; i < 24 * 60 * 60 * 2; i++) { if (isAdd) { lists.Add(temp); temp += 2; } else { lists.Add(temp); temp -= 2; } if (temp == 280) isAdd = false; if (temp == 20) isAdd = true; } canvas.Width = lists.Count; // Determine whether to show the thumb if (canvas.Width > CurvePanel.ActualWidth) { border.Visibility = Visibility.Visible; // Calculate the thumb width based on the ratio of ScrollViewer content border.Width = CurvePanel.ActualWidth * CurvePanel.ActualWidth / canvas.Width; } else { border.Visibility = Visibility.Collapsed; } curve.SetupData(lists); } private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) { curve.OffsetX(scroll.HorizontalOffset); Canvas.SetLeft(border, scroll.HorizontalOffset / ((lists.Count - CurvePanel.ActualWidth) / (CurvePanel.ActualWidth - border.ActualWidth))); } private bool isBorder = false; private void Border_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { isBorder = true; // Get the mouse position relative to the thumb point_border = e.GetPosition(border); } }The runtime effect is as follows.
