自定义TrackBar优化:缩小控件尺寸并实现单步多值滚动
Hey there! Let's work through the two key issues you're facing with your custom myTrackBar control: ditching the fixed width requirement and adding support for step-based value adjustments.
1. Removing the Fixed Width Constraint
Right now, your control relies on hardcoded values (like 1500 for the slider's max position) that force the width to be at least 1600. Instead, we'll calculate valid bounds dynamically based on the control's actual size and the slider's dimensions, so you can resize it freely while keeping the slider within the track.
2. Adding Step-Based Value Adjustment
To let users adjust the value by multiple increments with a single drag or click, we'll add a configurable Step property. We'll also make sure the slider snaps cleanly to these step increments for consistent behavior.
Modified Full Code
Public Class myTrackBar Inherits Control Private Pointer As New Bitmap(25, 30) Private Rect As New Rectangle(0, 0, 25, 30) Public Event Scroll(ByVal Sender As Object, ByVal e As ValueChangedEventArgs) Private Moving As Boolean Private Offset As Integer ' Configurable step for multi-value adjustments Public Property Step As Integer = 10 ' Make value range configurable instead of hardcoding 100-1600 Public Property Minimum As Integer = 100 Public Property Maximum As Integer = 1600 Public Sub New() DoubleBuffered = True ' Draw a slider that matches the bitmap's actual size Using g As Graphics = Graphics.FromImage(Pointer) g.Clear(Color.Transparent) g.FillRectangle(Brushes.DarkBlue, New Rectangle(0, 0, Pointer.Width, Pointer.Height)) End Using End Sub Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs) MyBase.OnPaint(e) e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality ' Draw track centered vertically across the full control width Dim trackY As Integer = (ClientSize.Height - 16) \ 2 e.Graphics.FillRectangle(Brushes.White, ClientRectangle) e.Graphics.DrawLine(New Pen(Brushes.Green, 16), New Point(0, trackY), New Point(ClientSize.Width, trackY)) ' Keep slider vertically centered Rect.Y = (ClientSize.Height - Rect.Height) \ 2 e.Graphics.DrawImage(Pointer, Rect) End Sub Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs) If Rect.Contains(e.Location) Then Moving = True Offset = (e.Location.X - Rect.X) Else ' Optional: Click track to jump slider to that position (snapped to step) Dim targetX As Integer = e.Location.X - (Rect.Width \ 2) UpdateSliderPosition(targetX) End If End Sub Protected Overrides Sub OnMouseUp(ByVal e As System.Windows.Forms.MouseEventArgs) Moving = False End Sub Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs) If Moving Then Dim targetX As Integer = e.Location.X - Offset UpdateSliderPosition(targetX) End If End Sub ' Helper to update slider position with bounds checking and step snapping Private Sub UpdateSliderPosition(targetX As Integer) Dim minX As Integer = 0 Dim maxX As Integer = ClientSize.Width - Rect.Width ' Clamp position to valid range targetX = Math.Min(Math.Max(targetX, minX), maxX) ' Snap to step increments if step is greater than 0 If Step > 0 Then Dim snappedValue As Integer = CInt(Math.Round((Value - Minimum) / Step) * Step) + Minimum Rect.X = ValueToXPosition(snappedValue) Else Rect.X = targetX End If Invalidate() RaiseEvent Scroll(Me, New ValueChangedEventArgs(Value)) End Sub ' Map value to slider X position Private Function ValueToXPosition(value As Integer) As Integer Dim valueRange As Integer = Maximum - Minimum Dim pixelRange As Integer = ClientSize.Width - Rect.Width If valueRange = 0 Then Return 0 Return CInt((value - Minimum) / valueRange * pixelRange) End Function ' Map slider X position to value Private Function XPositionToValue(x As Integer) As Integer Dim valueRange As Integer = Maximum - Minimum Dim pixelRange As Integer = ClientSize.Width - Rect.Width If pixelRange = 0 Then Return Minimum Return CInt((x / pixelRange) * valueRange) + Minimum End Function Public Property Value As Integer Get Return XPositionToValue(Rect.X) End Get Set(ByVal value As Integer) ' Clamp value to min/max range value = Math.Min(Math.Max(value, Minimum), Maximum) Rect.X = ValueToXPosition(value) Invalidate() RaiseEvent Scroll(Me, New ValueChangedEventArgs(Value)) End Set End Property ' Adjust slider position when control is resized Protected Overrides Sub OnResize(ByVal e As EventArgs) MyBase.OnResize(e) UpdateSliderPosition(Rect.X) End Sub End Class ' Keep your existing ValueChangedEventArgs class Public Class ValueChangedEventArgs Inherits EventArgs Public Property NewValue As Integer Public Sub New(newValue As Integer) Me.NewValue = newValue End Sub End Class
Key Changes Explained
Size Constraint Fix
- Dynamic Bounds: The slider's max position is now calculated as
ClientSize.Width - Rect.Width, so the control can be any width (as long as it's wider than the slider) without breaking functionality. - Centered Elements: Track and slider are vertically centered, making the control look polished regardless of height.
- Resize Handling: The
OnResizemethod ensures the slider stays within valid bounds if you resize the control after initialization.
Step Adjustment Fix
- Configurable Step: The
Stepproperty lets you define how much the value changes per increment (set it to 50, 100, etc., based on your needs). - Snapping Logic: The slider snaps to step increments automatically, so users get consistent, predictable adjustments.
- Track Click Support: Clicking anywhere on the track jumps the slider to that position (snapped to the step), matching standard TrackBar behavior.
- Flexible Range: Replaced hardcoded 100-1600 with
MinimumandMaximumproperties, making the control reusable for other ranges.
Testing the Control
- Set the control's width to any size (e.g., 400px) — the slider will move smoothly within the track while still covering the full 100-1600 value range.
- Adjust the
Stepproperty (e.g.,myTrackBar1.Step = 50) and you'll see the value jumps by 50 each time you drag or click the track.
内容的提问来源于stack exchange,提问作者TheNoob

