views:

1954

answers:

5

Was thinking it should be pretty easy to create a ProgressBar that drew some text upon itself. However, I am not quite sure what is happening here...

I added the following two overrides:

    protected override void OnPaintBackground(PaintEventArgs pevent)
    {
        base.OnPaintBackground(pevent);
        var flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.WordEllipsis;
        TextRenderer.DrawText(pevent.Graphics, "Hello", Font, Bounds, Color.Black, flags);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        var flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.WordEllipsis;
        TextRenderer.DrawText(e.Graphics, "Hello", Font, Bounds, Color.Black, flags);
    }

However, I get no text, and the methods doesn't even seem to be called. What is going on here?


Update: Thanks to the two answers so far, I have gotten it to actually call the OnPaint by using SetStyle(ControlStyles.UserPaint, true), and I have gotten it to draw the text in the right place by sending in new Rectangle(0, 0, Width, Height) instead of Bounds.

I do get text now, but the ProgressBar is gone... and the point was kind of to have the text on top of the ProgressBar. Any idea how I can solve this?

+1  A: 

Your problem is that you're passing in Bounds as your Rectangle parameter. Bounds contains the Height and Width of your control, which is what you want, but it also contains the Top and Left properties of your control, relative to the parent form, so your "Hello" is being offset on the control by however much your control is offset on its parent form.

Replace Bounds with new Rectangle(0, 0, this.Width, this.Height) and you should see your "Hello".

MusiGenesis
Oooh. Good catch.
Svish
+1  A: 
MaciekTalaska
Well, it actually is what I want to achieve. Problem is that when I put a label on top of it, the progressbar is hidden. I tried to create a transparent label, but that didn't work either, cause it disappeared once the progressbar started to change.
Svish
If you create a UserControl, try to place a ProgressBar first (and maybe 'SendItBack') then place a Label control onto ProgressBar. Should work. The problem is, how to make Label's background transparent - I am working on a solution for it at the moment...
MaciekTalaska
Hm, now that is interesting... will have to try this one out later :)
Svish
oh, I have posted it on my blog. You may also find a complete source there if you're interested: http://notonlyzeroesandones.site40.net/2009/10/05/transparent-windowsforms-controls-drawing-on-top-of-progressbar/
MaciekTalaska
A: 

I needed to do this myself and I thought that I would post a simplified example of my solution since I could not find any examples. It is actually pretty simple if you use the ProgressBarRenderer class:

class MyProgressBar : ProgressBar
{
    public MyProgressBar()
    {
        this.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        Rectangle rect = this.ClientRectangle;
        Graphics g = e.Graphics;

        ProgressBarRenderer.DrawHorizontalBar( g, rect );
        rect.Inflate(-3, -3);
        if ( this.Value > 0 )
        {
            Rectangle clip = new Rectangle( rect.X, rect.Y, ( int )Math.Round( ( ( float )this.Value / this.Maximum ) * rect.Width ), rect.Height );
            ProgressBarRenderer.DrawHorizontalChunks(g, clip);
        }

        // assumes this.Maximum == 100
        string text = this.Value.ToString( ) + '%';

        using ( Font f = new Font( FontFamily.GenericMonospace, 10 ) )
        {
            SizeF strLen = g.MeasureString( text, f );
            Point location = new Point( ( int )( ( rect.Width / 2 ) - ( strLen.Width / 2 ) ), ( int )( ( rect.Height / 2 ) - ( strLen.Height / 2 ) ) );
            g.DrawString( text, f, Brushes.Black, location ); 
        }
    }
}
Ed Swangren
and didn't my solution work for you? I have tested in on couple of different machines and it seemed to work fine on all of them.
MaciekTalaska
I didn't try it, I just posted the code I had used to draw a progress bar manually and then added the text bit.
Ed Swangren
A: 

You could override WndProc and catch the WmPaint message.

The example below paints the Text property of the progressbar in its center.

public class StatusProgressBar : ProgressBar
{
    const int WmPaint = 15;

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);

        switch (m.Msg)
        {
            case WmPaint:
                using (var graphics = Graphics.FromHwnd(Handle))
                {
                    var textSize = graphics.MeasureString(Text, Font);

                    using(var textBrush = new SolidBrush(ForeColor))
                        graphics.DrawString(Text, Font, textBrush, (Width / 2) - (textSize.Width / 2), (Height / 2) - (textSize.Height / 2));
                }
                break;
        }
    }
}
Elevenen
A: 

Here's another solution along with other people's suggestions. I subclassed the progressbar control to make this work. I mixed and matched codes from various places for this. The paint event could be cleaner, but that's for you to do ;)

   public class LabeledProgressBar: ProgressBar
   {
      private string labelText;

      public string LabelText
      {
         get { return labelText; }
         set { labelText = value; }
      }

      public LabeledProgressBar() : base()
      { 
         this.SetStyle(ControlStyles.UserPaint, true);
         this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);

         this.Paint += OnLabelPaint;
      }

      public void OnLabelPaint(object sender, PaintEventArgs e)
      {
         using(Graphics gr = this.CreateGraphics())
        {
         string str = LabelText + string.Format(": {0}%", this.Value);
         LinearGradientBrush brBG = new LinearGradientBrush(e.ClipRectangle, 
             Color.GreenYellow, Color.Green, LinearGradientMode.Horizontal);
         e.Graphics.FillRectangle(brBG, e.ClipRectangle.X, e.ClipRectangle.Y, 
             e.ClipRectangle.Width * this.Value / this.Maximum, e.ClipRectangle.Height);
         e.Graphics.DrawString(str, SystemFonts.DefaultFont,Brushes.Black,  
            new PointF(this.Width / 2 - (gr.MeasureString(str, SystemFonts.DefaultFont).Width / 2.0F),
            this.Height / 2 - (gr.MeasureString(str, SystemFonts.DefaultFont).Height / 2.0F)));
         }
       }
    }
Patratacus