0

I cobbled together a custom TrackBar with a percentage representing the current Slider position using the answers from here and here

My custom control is as follows:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace CustomControls
{
    public partial class TrackBarPercent : TrackBar
    {
        /// <summary>
        /// Initializes a new instance of <see cref="TrackBarPercent"/>.
        /// </summary>
        public TrackBarPercent() : base()
        {
            InitializeComponent();
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        }

        private Font _percentFont = SystemFonts.DefaultFont;
        private Color _percentColor = Color.Black;
        private SolidBrush _drawBrush = new SolidBrush(Color.Black);

        /// <summary>
        /// Gets or sets the font of the percent text.
        /// </summary>
        [Description("Gets or sets the font of the percent text.")]
        [Browsable(true)]
        public Font PercentFont
        {
            get { return _percentFont; }
            set { _percentFont = value; }
        }

        /// <summary>
        /// Gets or sets the color of the percent text.
        /// </summary>
        [Description("Gets or sets the color of the percent text.")]
        [Browsable(true)]
        public Color PercentColor
        {
            get { return _percentColor; }
            set { _percentColor = value; _drawBrush = new SolidBrush(_percentColor); }
        }

        private Rectangle Slider
        {
            get
            {
                try
                {
                    RECT rc = new RECT();
                    SendMessageRect(this.Handle, TBM_GETTHUMBRECT, IntPtr.Zero, ref rc);
                    return new Rectangle(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
                }
                catch
                {
                    return new Rectangle();
                }
            }
        }

        private const int TBM_GETTHUMBRECT = 0x400 + 25;
        private struct RECT { public int left, top, right, bottom; }
        [DllImport("user32.dll", EntryPoint = "SendMessageW")]
        private static extern IntPtr SendMessageRect(IntPtr hWnd, int msg, IntPtr wp, ref RECT lp);

        private int lastValue = 0;
        protected override void WndProc(ref Message m)
        {
            try
            {
                base.WndProc(ref m);

                if (m.Msg == 0x0F)
                {
                    if (this.Value != lastValue)
                    {
                        lastValue = this.Value;
                        this.Invalidate();
                    }

                    using (Graphics graphics = Graphics.FromHwnd(Handle))
                    {
                        DrawPercentText(graphics);
                    }
                }
            }
            catch { }
        }

        private void DrawPercentText(Graphics g)
        {
            try
            {
                Point pctTxtPoint;

                if (this.Orientation == System.Windows.Forms.Orientation.Horizontal)
                {
                    pctTxtPoint = new Point((Slider.Left + Slider.Right) / 2, Slider.Bottom);
                }
                else
                {
                    pctTxtPoint = new Point(Slider.Right + 5, Slider.Top - 2);
                }

                double percent = (double)this.Value * 100.0 / (double)this.Maximum;
                string pctStr = string.Format("{0:0.##}%", percent);

                g.DrawString(pctStr, _percentFont, _drawBrush, pctTxtPoint);
            }
            catch { }
        }

        [DllImport("user32.dll")]
        public extern static int SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);

        private static int MakeParam(int loWord, int hiWord)
        {
            return (hiWord << 16) | (loWord & 0xffff);
        }

        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            SendMessage(this.Handle, 0x0128, MakeParam(1, 0x1), 0);
        }
    }
}

This works, for the most part, but the text gets redrawn every time I hover my mouse over the slider causing the text to look bold. This effect goes away when another control gets focus. Additionally, I'm getting periodic exceptions thrown by the control that I do not understand.

I keep track of the last value that was set for this control so that it invalidates the view if the value has changed. I needed this to prevent the control from drawing the percentage text over the previous text. I don't think this approach was very elegant but I'm not sure how to go about doing it better.

How can I fix this code up so that the text doesn't become "bold" and fix the exceptions I'm getting?

Kashif
  • 865
  • 2
  • 12
  • 33
  • 1
    I sympathize with your attempts to get this right and hope someone will help you but I must say that I always found it so much easier to use a nested Label, Docked to the Bottom with Centered Text.. – TaW Jun 23 '17 at 07:00
  • Thank you. I appreciate the thought. If I can't get this to work, that's exactly what I'll use – Kashif Jun 23 '17 at 15:26

0 Answers0