0

I am trying to draw a string over a progress bar as outlined here and here. The progress bar is located on one of a series of tab pages in my tab control.

This is my code:

int percent = 55;
using (Graphics gr = progressBar1.CreateGraphics())
{
gr.DrawString(percent.ToString() + "%",
SystemFonts.DefaultFont,
Brushes.Black,
new PointF(progressBar1.Width / 2 - (gr.MeasureString(percent.ToString() + "%", SystemFonts.DefaultFont).Width / 2.0F), progressBar1.Height / 2 - (gr.MeasureString(percent.ToString() + "%", SystemFonts.DefaultFont).Height / 2.0F)));
}

It doesn't work. After fiddling with a timer:

this.timer1.Enabled = true;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);

private void timer1_Tick(object sender, EventArgs e)
{
    progressBar1.PerformStep();
    System.Threading.Thread.Sleep(100);
    progressBar1.Refresh();
    int percent = (int)(((double)(progressBar1.Value - progressBar1.Minimum) / (double)(progressBar1.Maximum - progressBar1.Minimum)) * 100);
    progressBar1.CreateGraphics().DrawString(percent.ToString() + "%", new Font("Arial", (float)8.25, FontStyle.Regular), Brushes.Black, new PointF(progressBar1.Width / 2 - 10, progressBar1.Height / 2 - 7));
}

and just some simple:

private void button1_Click(object sender, EventArgs e)
{
    DrawProgress(); //this just calls the a method to do the DrawString
}

I found out it does draw the string but after switching to another tab and back the string is no longer visible/there.

I even switched "gr" to "progressBar1.CreateGraphics()" and removed the "using" statement to see if it was just disposing of the string, but even with:

progressBar1.CreateGraphics().DrawString(percent.ToString() + "%", SystemFonts.DefaultFont, Brushes.Black, new PointF(progressBar1.Width / 2 - (progressBar1.CreateGraphics().MeasureString(percent.ToString() + "%", SystemFonts.DefaultFont).Width / 2.0F), progressBar1.Height / 2 - (progressBar1.CreateGraphics().MeasureString(percent.ToString() + "%", SystemFonts.DefaultFont).Height / 2.0F)));

I have the same problem.

TaW
  • 53,122
  • 8
  • 69
  • 111
mathgenius
  • 503
  • 1
  • 6
  • 21
  • _anyControl.CreateGraphics()_ is usually wrong. This is no exception. Never use `control.CreateGraphics`! Either draw into a `Bitmap bmp` using a `Graphics g = Graphics.FromImage(bmp)` or in the `Paint` event of a control, using the `e.Graphics` parameter.. – TaW Jun 07 '16 at 22:18
  • @TaW, thank you, I will look into that. Could you tell me why is that so? In addition I wonder why this question was voted down, I provided plenty of evidence, resources, structured it well and have tried everything before asking. – mathgenius Jun 08 '16 at 09:45
  • What you have failed to do is proper research. This problem gets asked all the time and there are many many answers, so folks get weary to see the same question again and again.. See [here for an explanation](http://stackoverflow.com/questions/35544789/drawtobitmap-returning-blank-image/35571419?s=4|0.0000#35571419) – TaW Jun 08 '16 at 10:00
  • You also show code but don't tell us where it sits and from where it is called.. - Note that both links you show indeed use the problematic code to get at a Graphics object. Their reasoning probably is that they do not care for persistency since the progress is supposed to happen before anything invaidates the Graphics. Which in a little too optimistic imo, as your tab-switching shows.. – TaW Jun 08 '16 at 10:06
  • A final note: I always cringe when I see code here that makes me scroll horizontally. Adding a few line breaks and a few spaces makes it so much easier to read.. – TaW Jun 08 '16 at 10:08
  • @TaW, huh I guess you are right about the horizontal scroll, I'll see to it I don't do that again. I am not showing where the code sits or where it is called from, because that is not important in this case, mainly because that *is* the code, I made a new project where this is (almost) the entire code and it behaves the same. I also did search for other questions similar to mine, but did not find any that are relevant. The one you linked is about the same method returning a blank image, while my problem is that the drawing disappears, that may be the same thing, but I sure can't tell that. – mathgenius Jun 08 '16 at 16:03
  • Seeeing where a piece of code sits helps understand when or how it is called. The issue with the linked post is __exactly__ yours, although it looks different. Both times the drawing code is not where it belongs, i.e. the Paint event. Your additional problem is that PBar doesn't even expose it Paint event. But using the volatile graphics will still not work, as you have experienced. Jeroen's answer works fine, I suggest you try it. – TaW Jun 08 '16 at 16:09
  • @TaW, like I said "[...]but **I** sure **can't** tell that." meaning that since I'm not that good at it I have no way of knowing that that question is the same issue as mine and will, therefore, help me. Hence I think it's unfair to hold it against me that I asked a separate question about it. Thank you for your help and I will surely try Mr. Langen's answer, I just haven't had the time so far. – mathgenius Jun 08 '16 at 16:14
  • Yes, good luck with it. However you need to understand about SO that dwonvoting is not meant to hold anything against __you__. Not you but the question was downvoted and you really really must not let that get at you!! It happend to all of us at the start and is just a way to make SO the most useful archive it is.. (And, no, not all downvoters are fair.) – TaW Jun 08 '16 at 16:23

1 Answers1

2

I think, the best solution is: You should derive from ProgressBar and implement your own Paint handler.

Here's an example: (tested)

// Custom paint progressbar
public class ProgressBarWithTitle : ProgressBar
{
    private Brush _barBrush;


    public ProgressBarWithTitle()
    {
        this.SetStyle(ControlStyles.UserPaint, true);
        this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);

        _barBrush = new SolidBrush(this.ForeColor);

    }

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

        var rect = new RectangleF(0, 0, (float)(this.Width * (Value - Minimum) / Maximum), this.Height);
        e.Graphics.FillRectangle(_barBrush, rect);

        var valueString = base.Value.ToString() + "%";

        var textSize = e.Graphics.MeasureString(valueString, SystemFonts.DefaultFont);

        e.Graphics.DrawString(
            valueString,
            SystemFonts.DefaultFont,
            Brushes.Black,
            new PointF(
                (Width / 2) - ((int)textSize.Width / 2),
                (Height / 2) - ((int)textSize.Height / 2)));
    }


}

The only problem is, that you'll have to draw the progressbar yourself.

Jeroen van Langen
  • 21,446
  • 3
  • 42
  • 57