Tatami
Tatami

Reputation: 105

How do I make the "Custom Trackbar" work like the "Standard Trackbar" (accept negative numbers so that the slider doesn't go outside the scrollbar)?

There is a "Custom Trackbar", which can take negative and positive values. If you set Min = -50, Max = 100, the slider moves outside the scrollbar. I need it to behave in the same way as "Standard Trackbar" (it should not go beyond the scrollbar boundaries). How to do it?

The screenshot shows 2 Trackbars for both I set (Minimum = -50, Maximum = 100, Value = -50), but after building the project I got the following picture:

enter image description here

If we set (Minimum = 0, Maximum = 100, Value = 25), we get the following:

enter image description here

[Code Custom Trackbar]

[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;

            //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;

            //if (thumbSize.Width % 2 == 0 && thumbSize.Width > 0) thumbSize.Width += 1;

            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) => {
            // When clicking on the ScrollBar, center the Thumb relative to the mouse cursor
            if (!ThumbRect.Contains(e.Location)) {
                MoveThumb(e, false);
            }

            // When clicking on Thumb, determine the startMouseClickPosition
            if (ThumbRect.Contains(e.Location)) {
                startMouseClickPosition.X = e.X - ThumbRect.Left; // OR ... - ThumbRect.X 
                ThumbBackColor = Color.Green;
            }
        };

        this.MouseMove += (sender, e) => {
            ThumbBackColor = ThumbRect.Contains(e.Location)
            ? ThumbBackColor = Color.Orange : ThumbBackColor = Color.Gray;

            if (e.Button == MouseButtons.Left) {
                ThumbBackColor = Color.Green; MoveThumb(e);
            }
        };

        this.MouseLeave += (sender, e) => { ThumbBackColor = Color.Gray; };
    }

    int PaddingLR = 10;
    // padding(left/right) must be the same,
    // if the orientation of the scroll bar is HORIZONTAL

    private void MoveThumb(MouseEventArgs e, bool useStartMouseClickPosition = true) {
        double newValue;

        if (useStartMouseClickPosition) {
            currentMousePosition.X = e.X - startMouseClickPosition.X;

            // works correctly
            newValue = Maximum * (currentMousePosition.X - (ThumbSize.Width / 2) + (ThumbSize.Width / 2) - PaddingLR)
                / (Width - ThumbSize.Width - PaddingLR * 2);
        } else {
            newValue = Maximum * (e.X - ThumbSize.Width / 2 - PaddingLR)
                / (Width - ThumbSize.Width - PaddingLR * 2);
        }

        // does NOT work correctly (although the calculation result is the same)
        //double newValue = Maximum * (newThumbLeft + (ThumbSize.Width / 2) - PaddingLR) /
        //    (Width - ThumbSize.Width - PaddingLR * 2);

        Value = Math.Max(0, 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 * (Width - ThumbSize.Width - Padding.Left * 2) / Maximum + Padding.Right),

            0 + Padding.Top,

            ThumbSize.Width, // fixed slider width

            Height - Padding.Bottom - Padding.Top // dynamic slider height
            // (example) Height - 4, means to move the slider by 2 px above and below
        );

        // Filling the scroll bar
        using (SolidBrush brush = new SolidBrush(TrackBackColor)) {
            e.Graphics.FillRectangle(brush, new Rectangle(0, 0, Width, Height));
        }

        // The colored line in front of the slider
        using (Pen pen = new Pen(TrackBorderColor2, TrackThickness)) {
            e.Graphics.DrawLine(pen, Padding.Left, Height / 2, Width - Padding.Right, Height / 2);
        }

        // The colored line following the slider
        using (Pen pen = new Pen(TrackBorderColor, TrackThickness)) {
            e.Graphics.DrawLine(pen, Padding.Left, Height / 2, ThumbRect.Right, Height / 2);
        }

        // Filling the slider
        using (SolidBrush brush2 = new SolidBrush(Color.FromArgb(100, 0, 0, 0))) {
            e.Graphics.FillRectangle(brush2, ThumbRect);
        }
    }
    #endregion
}

Upvotes: 0

Views: 375

Answers (1)

Tatami
Tatami

Reputation: 105

Thanks to the help of user @IVSoftware, in writing auxiliary methods calcValueFromPosition() and calcXfromValue(), the following solution was obtained, which allows you to set different paddings:

enter image description here

[Code Custom Trackbar]

namespace Handy_UI.Controls.Trackbars {
    [DefaultEvent("ValueChanged")]
    public class HandyHTrackbarWorked : Control {
        #region Setting the initial parameters
        public HandyHTrackbarWorked() {
            SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint
                | ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw, true); UpdateStyles();

            Size = new Size(250, 12);
            ThumbSize = new Size(15, 12);
        }
        #endregion

        #region Main features
        private double _value;
        public double Value {
            get { return _value; }
            set {
                _value = value;
                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 Properties responsible for design
        private Size thumbSize;
        public Size ThumbSize {
            get { return thumbSize; }
            set {
                thumbSize = value;
                Invalidate();
            }
        }

        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(); }
        }

        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(); }
        }
        #endregion

        #region Key Events
        public event EventHandler ValueChanged;
        #endregion

        #region Обработчики событий
        private Point startMouseClickPosition;
        private Point currentMousePosition;

        protected override void OnCreateControl() {
            base.OnCreateControl();

            this.MouseDown += (sender, e) => {
                // When clicking on the ScrollBar, center the Thumb relative to the mouse cursor
                if (!ThumbRect.Contains(e.Location)) {
                    MoveThumb(e, false);
                }

                // When clicking on Thumb, determine the startMouseClickPosition
                if (ThumbRect.Contains(e.Location)) {
                    startMouseClickPosition.X = e.X - ThumbRect.Left; // OR ... - ThumbRect.X 
                    ThumbBackColor = Color.Green;
                }
            };

            this.MouseMove += (sender, e) => {
                ThumbBackColor = ThumbRect.Contains(e.Location)
                ? ThumbBackColor = Color.Orange : ThumbBackColor = Color.Gray;

                if (e.Button == MouseButtons.Left) {
                    ThumbBackColor = Color.Green; MoveThumb(e);
                }
            };

            this.MouseLeave += (sender, e) => { ThumbBackColor = Color.Gray; };

            //this.SizeChanged += (sender, e) => { TrackThickness = Height - Padding.Bottom - Padding.Top; };
        }

        private void MoveThumb(MouseEventArgs e, bool useStartMouseClickPosition = true) {
            Point currentMousePosition = new Point(0, 0);

            if (useStartMouseClickPosition) {
                currentMousePosition.X = e.X - startMouseClickPosition.X + ThumbSize.Width / 2
                    - Padding.Right - (Padding.Left - Padding.Right);
            } else currentMousePosition.X = e.X - Padding.Left;

            Value = calcValueFromPosition(currentMousePosition);
        }

        private double calcValueFromPosition(Point e) {
            var mouseRange = Width - (Padding.Left + Padding.Right);
            var pct = e.X / (double)mouseRange;

            var controlRange = Maximum - Minimum;
            var relative = pct * controlRange;
            var value = Minimum + relative;

            value = Math.Max(Minimum, value);
            value = Math.Min(Maximum, value);

            return value;
        }

        public void OnScroll() {
            ValueChanged?.Invoke(this, EventArgs.Empty);
        }
        #endregion

        #region Drawing controls
        public int calcXfromValue() {
            var range = Maximum - Minimum;
            var relative = Value - Minimum;
            var pct = relative / range;
            var width = Width - (Padding.Left + Padding.Right);
            var pos = pct * width;

            var x = pos + Padding.Left - (ThumbSize.Width / 2);

            if (x < 0 + Padding.Left) x = 0 + Padding.Left;
            else if (x > Width - ThumbSize.Width - Padding.Right) x = Width - ThumbSize.Width - Padding.Right;

            return (int)x;
        }

        public Rectangle ThumbRect => new Rectangle(
            x: calcXfromValue(), y: 0 + Padding.Top,
            width: ThumbSize.Width, // fixed slider width
            height: Height - Padding.Bottom - Padding.Top // dynamic slider width
        );

        protected override void OnPaint(PaintEventArgs e) {
            // Filling the scroll bar
            using (SolidBrush brush = new SolidBrush(TrackBackColor)) {
                e.Graphics.FillRectangle(brush, new Rectangle(0, 0, Width, Height));
            }

            // The colored line in front of the slider
            using (Pen pen = new Pen(TrackBorderColor2, TrackThickness)) {
                e.Graphics.DrawLine(pen, Padding.Left, Height / 2, Width - Padding.Right, Height / 2);
            }

            // The colored line following the slider
            using (Pen pen = new Pen(TrackBorderColor, TrackThickness)) {
                e.Graphics.DrawLine(pen, Padding.Left, Height / 2, ThumbRect.Right, Height / 2);
            }

            // Filling the slider
            using (SolidBrush brush2 = new SolidBrush(Color.FromArgb(100, 0, 0, 0))) {
                e.Graphics.FillRectangle(brush2, ThumbRect);
            }
        }
        #endregion
    }
}

Upvotes: 0

Related Questions