C#: Overriding OnPaint on ProgressBar not working?

早过忘川 提交于 2019-11-27 23:13:31

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".

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

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

It seems that if you call 'SetStyle(ControlStyles.UserPaint, true)' the standard OnPaint method implemented for ProgressBar could not be invoked (using base.OnPaint(e) does not work at all). The strangest thing is that even if you actually create a UserControl, and try to draw draw some text upon the progress bar... it doesn't seem to work too... Of course you may place a Label on top of it... but I suppose it is not actually what you wanted to achieve.

Ok, it seems that I have managed to solve this problem. It is although a little complicated. First you need to create a transparent Label control. Code below:

public class TransparentLabel : System.Windows.Forms.Label
{
    public TransparentLabel()
    {
        this.SetStyle(ControlStyles.Opaque, true);
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
    }

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x20;
            return cp;
        }
    }
}

Second thing is to create UserControl, place a ProgressBar on it (Dock=Fill) - this will be the control that we will use instead of standard ProgressBar. Code:

public partial class UserControl2 : UserControl
{
    public UserControl2()
    {
        InitializeComponent();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        this.progressBar1.SendToBack();
        this.transparentLabel1.BringToFront();
        this.transparentLabel1.Text = this.progressBar1.Value.ToString();
        this.transparentLabel1.Invalidate();
   }

    public int Value
    {
        get { return this.progressBar1.Value; }
        set
        {
            this.progressBar1.Value = value; 
        }
    }
}

The strange thing with ProgressBar is that it 'overdraws' the controls that are being placed upon it, so it is needed to send progressbar to back, and bring the label control to front. I haven't found more elegant solution at the moment. This works, the label is being displayed on the progressbar, the background of the label control is transparent, so I think it looks like you wanted it to look :)

I may share my sample code if you wish...

Oh, btw. this strange behaviour of ProgressBar control that I have mentioned, is responsible for that it is not possible to use Graphics object to draw anything on a control that derives from ProgressBar. The text (or whatever you draw using Graphics object) is actually being drawn but... behind the ProgressBar control (if you take a closer look, you may see this user drawn things flickering when the Value of the ProgressBar changes and it need to repaint itself).

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)));
         }
       }
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!