如何让自定义Trackbar支持负数且滑块不超出轨道边界?
自定义Trackbar正负值滑块越界修复方案
问题说明
自定义Trackbar支持正负数值,但设置Minimum=-50、Maximum=100时,滑块会超出轨道边界,需要让其表现与标准Trackbar一致,滑块始终保持在轨道范围内。
核心修复步骤
1. 恢复Value属性的边界校验
取消Value属性中边界判断的注释,确保Value始终被限制在Minimum与Maximum之间:
public double Value { get { return _value; } set { _value = value; // 恢复边界校验,确保Value不超出范围 if (_value < Minimum) { _value = Minimum; } if (_value > Maximum) { _value = Maximum; } OnScroll(); Refresh(); } }
2. 修正滑块位置计算逻辑
在OnPaint方法中,滑块X坐标的计算需要基于数值区间范围(Maximum - Minimum),而非直接使用Maximum,适配正负值场景:
ThumbRect = new Rectangle( // 修正后的滑块X坐标计算 Convert.ToInt32((Value - Minimum) * (Width - ThumbSize.Width - Padding.Left - Padding.Right) / (Maximum - Minimum) + Padding.Left), 0 + Padding.Top, ThumbSize.Width, Height - Padding.Bottom - Padding.Top );
3. 修正鼠标拖动/点击的数值计算
MoveThumb方法中,newValue的计算要适配正负区间,同时边界判断要包含Minimum:
private void MoveThumb(MouseEventArgs e, bool useStartMouseClickPosition = true) { double newValue; // 计算轨道可用宽度 double trackAvailableWidth = Width - ThumbSize.Width - PaddingLR * 2; if (useStartMouseClickPosition) { currentMousePosition.X = e.X - startMouseClickPosition.X; // 基于区间范围计算newValue newValue = Minimum + (Maximum - Minimum) * (currentMousePosition.X - PaddingLR) / trackAvailableWidth; } else { // 点击轨道时的数值计算 newValue = Minimum + (Maximum - Minimum) * (e.X - ThumbSize.Width / 2 - PaddingLR) / trackAvailableWidth; } // 修正边界判断,包含Minimum Value = Math.Max(Minimum, Math.Min(Maximum, newValue)); }
4. 修正轨道填充线绘制
原来的已填充轨道线绘制到滑块右边界,适配正负值时需要改为基于数值比例计算终点:
// 计算已填充轨道的终点位置 int filledTrackEnd = Padding.Left + Convert.ToInt32((Value - Minimum) * (Width - Padding.Left - Padding.Right) / (Maximum - Minimum)); // 绘制已填充的轨道线 using (Pen pen = new Pen(TrackBorderColor, TrackThickness)) { e.Graphics.DrawLine(pen, Padding.Left, Height / 2, filledTrackEnd, Height / 2); }
完整修正后的代码
[DefaultEvent("ValueChanged")] public class HandyHTrackbarWorked : Control { #region 初始化参数 public HandyHTrackbarWorked() { SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw, true); UpdateStyles(); Size = new Size(250, 12); ThumbSize = new Size(ThumbRect.Width = 15, ThumbRect.Height = 12); } #endregion #region 核心属性 private double _value; public double Value { get { return _value; } set { _value = value; // 恢复边界校验,确保Value不超出范围 if (_value < Minimum) { _value = Minimum; } if (_value > Maximum) { _value = Maximum; } OnScroll(); Refresh(); } } private double minimum; public double Minimum { get { return minimum; } set { minimum = value; Invalidate(); } } private double maximum = 100; public double Maximum { get { return maximum; } set { maximum = value; Invalidate(); } } private double smallStep = 1; public double SmallStep { get { return smallStep; } set { smallStep = (value > 0) ? value : 1; } } #endregion #region 外观属性 [Description("滑块尺寸")] private Size thumbSize; public Size ThumbSize { get { return thumbSize; } set { thumbSize = value; Invalidate(); } } [Description("滑块颜色")] private Color thumbBackColor = Color.FromArgb(255, 255, 255); public Color ThumbBackColor { get { return thumbBackColor; } set { thumbBackColor = value; Invalidate(); } } private Color trackBackColor = Color.Transparent; public Color TrackBackColor { get { return trackBackColor; } set { trackBackColor = value; Invalidate(); } } private Color trackBorderColor = Color.FromArgb(221, 0, 49); public Color TrackBorderColor { get { return trackBorderColor; } set { trackBorderColor = value; Invalidate(); } } private Color trackBorderColor2 = Color.FromArgb(64, 64, 64); public Color TrackBorderColor2 { get { return trackBorderColor2; } set { trackBorderColor2 = value; Invalidate(); } } [Description("轨道厚度")] private int trackThickness = 2; public int TrackThickness { get { return trackThickness; } set { trackThickness = value; Invalidate(); } } public new Padding Padding { get { return base.Padding; } set { base.Padding = value; Invalidate(); } } public Rectangle ThumbRect; #endregion #region 事件定义 public event EventHandler ValueChanged; #endregion #region 事件处理 private Point startMouseClickPosition; private Point currentMousePosition; protected override void OnCreateControl() { base.OnCreateControl(); this.MouseDown += (sender, e) => { // 点击轨道时,将滑块居中到鼠标位置 if (!ThumbRect.Contains(e.Location)) { MoveThumb(e, false); } // 点击滑块时,记录鼠标相对滑块的位置 if (ThumbRect.Contains(e.Location)) { startMouseClickPosition.X = e.X - ThumbRect.Left; ThumbBackColor = Color.Green; } }; this.MouseMove += (sender, e) => { ThumbBackColor = ThumbRect.Contains(e.Location) ? Color.Orange : Color.Gray; if (e.Button == MouseButtons.Left) { ThumbBackColor = Color.Green; MoveThumb(e); } }; this.MouseLeave += (sender, e) => { ThumbBackColor = Color.Gray; }; } int PaddingLR = 10; private void MoveThumb(MouseEventArgs e, bool useStartMouseClickPosition = true) { double newValue; double trackAvailableWidth = Width - ThumbSize.Width - PaddingLR * 2; if (useStartMouseClickPosition) { currentMousePosition.X = e.X - startMouseClickPosition.X; newValue = Minimum + (Maximum - Minimum) * (currentMousePosition.X - PaddingLR) / trackAvailableWidth; } else { newValue = Minimum + (Maximum - Minimum) * (e.X - ThumbSize.Width / 2 - PaddingLR) / trackAvailableWidth; } Value = Math.Max(Minimum, Math.Min(Maximum, newValue)); } public void OnScroll() { ValueChanged?.Invoke(this, EventArgs.Empty); } #endregion #region 绘制逻辑 protected override void OnPaint(PaintEventArgs e) { ThumbRect = new Rectangle( Convert.ToInt32((Value - Minimum) * (Width - ThumbSize.Width - Padding.Left - Padding.Right) / (Maximum - Minimum) + Padding.Left), 0 + Padding.Top, ThumbSize.Width, Height - Padding.Bottom - Padding.Top ); // 填充轨道背景 using (SolidBrush brush = new SolidBrush(TrackBackColor)) { e.Graphics.FillRectangle(brush, new Rectangle(0, 0, Width, Height)); } // 绘制完整轨道线 using (Pen pen = new Pen(TrackBorderColor2, TrackThickness)) { e.Graphics.DrawLine(pen, Padding.Left, Height / 2, Width - Padding.Right, Height / 2); } // 绘制已填充的轨道线 int filledTrackEnd = Padding.Left + Convert.ToInt32((Value - Minimum) * (Width - Padding.Left - Padding.Right) / (Maximum - Minimum)); using (Pen pen = new Pen(TrackBorderColor, TrackThickness)) { e.Graphics.DrawLine(pen, Padding.Left, Height / 2, filledTrackEnd, Height / 2); } // 填充滑块 using (SolidBrush brush2 = new SolidBrush(Color.FromArgb(100, 0, 0, 0))) { e.Graphics.FillRectangle(brush2, ThumbRect); } } #endregion }
内容的提问来源于stack exchange,提问作者Tatami
相关产品推荐
相关产品推荐

