-1

I'm working on a GUI for a CNC machine. We have load cells that output a voltage depending on how much force is applied to the cell, which the machine can read and then display to the operator so they know how much force they are clamping a part with.

Microsoft's website says .NET Framework 4.6.1 (which I'm building with) progress bars have a text property, but setting the text itself doesn't display it. I found a different way of doing it like this:

int loadVal = 0;
string progBarText = "";
SizeF textSize;
Graphics graphics = CreateGraphics();
Font font = new Font("Lucida Console", FontHeight = 11, FontStyle.Regular);

leftClampProgBar.SuspendLayout();
rightClampProgBar.SuspendLayout();


            

//~~~~Left Clamp~~~~~~
loadVal = (PLC_ushLeftClampLoad * 500) / 65535;
leftClampProgBar.Value = (loadVal * 100) / 500;

//setting the text for the progress bar
progBarText = loadVal.ToString() + " Lb(s)";

//have to figure out how big the text is
textSize = graphics.MeasureString(progBarText, font);

//drawing the text to the progress bar
leftClampProgBar.CreateGraphics().DrawString(
    progBarText,
    font,
    Brushes.Black,
    new PointF((leftClampProgBar.Width - textSize.Width) / 2,
    (leftClampProgBar.Height - textSize.Height) / 2));;


//~~~~~Right Clamp~~~~~~
loadVal = (PLC_ushRightClampLoad * 500) / 65535;
rightClampProgBar.Value = (loadVal * 100) / 500;

//setting the text for the progress bar
progBarText = loadVal.ToString() + " Lb(s)";

//have to figure out how big the text is
textSize = graphics.MeasureString(progBarText, font);

//drawing the text to the progress bar
rightClampProgBar.CreateGraphics().DrawString(
    progBarText,
    font,
    Brushes.Black,
    new PointF((rightClampProgBar.Width - textSize.Width) / 2, 
    (rightClampProgBar.Height - textSize.Height) / 2));

//AddNotification("Right Clamp: " + loadVal, Color.Purple);
leftClampProgBar.ResumeLayout();
rightClampProgBar.ResumeLayout();

However, this leads to the the text sometimes being printed wrong, or it is not refreshing correctly. The method the code above is in gets called by a timer every 500ms and causes the GUI to act a little slower than before. I could make a different timer for this specifically that has a larger interval, but I wanted to know if there was a more efficient way to display the text at all, not worrying about how often the timer repaints it. progress bar text

FinnJ
  • 1
  • 3
  • `CreateGraphics` is almost always the wrong way to go about it and it should never be a global object. Do all your painting in the Paint event which will thoughtfully supply you the appropriate `Graphics` object. It is also not clear where that code is, so we have no context – Ňɏssa Pøngjǣrdenlarp Mar 22 '22 at 19:57
  • You can try [this one](https://stackoverflow.com/a/66521254/7444103), or [this other](https://stackoverflow.com/a/62053257/7444103) or [this simple version](https://stackoverflow.com/a/51466627/7444103) -- You can draw text on any of those. – Jimi Mar 22 '22 at 20:14
  • @Ňɏssa The issue OP has is ProgressBar does not fire the Paint event, so they are going about this another way. The reason for their weird printing is that they don't clear the prior text before drawing new. – Adam Mar 22 '22 at 20:15
  • @ŇɏssaPøngjǣrdenlarp this code is in a method that gets called by a timer object. Both of which are children to the windows form the GUI is running in, so there isn't any global object really being used here – FinnJ Mar 22 '22 at 20:28
  • 1
    @Adam The ProgressBar does rise the Paint event, just not as the default behavior. See the first link I posted. – Jimi Mar 22 '22 at 20:36
  • @Jimi that only occurs because ProgressBar class is modified by `this.SetStyle(ControlStyles.UserPaint, true);` – Adam Mar 22 '22 at 20:48
  • @Adam Yes, that's what I said: *not the default behavior*. But still a behavior that you have explicit means to override (as with any other Control). E.g., a Panel is not double-buffered by default, but you can create a Custom Control that changes this behavior. Or make it transparent, or semi transparent or auto-scroll without scrollbars or whatever else. – Jimi Mar 22 '22 at 20:51

2 Answers2

0

I would recommend just having a separate label above or to the side of the progress-bar that you update. This should be far simpler, and probably also being easier to read.

If you insist on printing the text on top of the progress-bar you should consider creating your own class that derives from progress-bar and override the onPaint method. But if you use this approach you need to consider contrast and legibility as the text and progress overlaps.

JonasH
  • 28,608
  • 2
  • 10
  • 23
  • I considered this, but I didn't know if it would make the designer unhappy with having two things on top of each other and I know that some devs have used text overtop progressBars, which I assumed was using the text property of the class – FinnJ Mar 22 '22 at 22:13
  • @FinnJ you might want to check out uxstackexchange [Progress bar design with text overlay](https://ux.stackexchange.com/questions/136517/progress-bar-design-with-text-overlay) – JonasH Mar 23 '22 at 06:48
0

You could create custom progress bar to do this. The following class assumes ProgressBarRenderer.IsSupported results in true and does not animate progress bar.

public class TextProgressBar : ProgressBar
{
    public TextProgressBar() : base()
    {
        SetStyle(ControlStyles.UserPaint, true);
        DoubleBuffered = true; // remove flicker
    }

    // unhide Text/Font Properties and force changes to re-render control

    [Browsable(true)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public override string Text
    {
        get => base.Text;
        set
        {
            base.Text = value;
            Refresh();
        }
    }

    [Browsable(true)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public override Font Font
    {
        get => base.Font;
        set
        {
            base.Font = value;
            if (!string.IsNullOrWhiteSpace(Text)) Refresh();
        }
    }

    protected override void OnPaintBackground(PaintEventArgs pevent)
    {
        base.OnPaintBackground(pevent);

        // draw progress bar background
        ProgressBarRenderer.DrawHorizontalBar(pevent.Graphics, new Rectangle(0, 0, Width, Height));
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);

        // draw progress on progress bar
        double percentage = ((double)(Value - Minimum)) / ((double)(Maximum - Minimum));
        ProgressBarRenderer.DrawHorizontalChunks(e.Graphics, new Rectangle(0, 0, (int)(Width * percentage), Height));

        // draw text on progress bar
        using (Brush brush = new SolidBrush(ForeColor))
        {
            // get rendered size of text
            var size = e.Graphics.MeasureString(Text, Font, new SizeF(Width, Height));

            // calculate location to center text on progress bar
            var location = new PointF((Width - size.Width) * 0.5f, (Height - size.Height) * 0.5f);

            // draw text
            e.Graphics.DrawString(Text, Font, brush, new RectangleF(location, size));
        }
    }
}
Adam
  • 562
  • 2
  • 15