C#: Overriding OnPaint on ProgressBar not working?
Solution 1
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".
Solution 2
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;
}
}
}
Solution 3
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 );
}
}
}
Solution 4
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).
Lemon
Software Developer, Geek, HSP, SDA, ..., open, honest, careful, perfectionist, ... Currently into indoor rowing and rock climbing, just to mention something non-computer-related... Not the best at bragging about myself... so... not sure what more to write... 🤔
Updated on June 13, 2022Comments
-
Lemon almost 2 years
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 usingSetStyle(ControlStyles.UserPaint, true)
, and I have gotten it to draw the text in the right place by sending innew Rectangle(0, 0, Width, Height)
instead ofBounds
.I do get text now, but the
ProgressBar
is gone... and the point was kind of to have the text on top of theProgressBar
. Any idea how I can solve this? -
Lemon over 14 yearsWell, 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.
-
Maciek Talaska over 14 yearsIf 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...
-
Lemon over 14 yearsHm, now that is interesting... will have to try this one out later :)
-
Maciek Talaska over 14 yearsoh, I have posted it on my blog. You may also find a complete source there if you're interested: notonlyzeroesandones.site40.net/2009/10/05/…
-
Maciek Talaska about 14 yearsand 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.
-
Ed S. about 14 yearsI didn't try it, I just posted the code I had used to draw a progress bar manually and then added the text bit.
-
Clinton Ward over 11 yearsThis is the best solution i've seen.
-
Kosmo零 over 10 yearsThis is the best solution for sure
-
pradeepradyumna almost 6 yearsI loved this. This is exactly what I was looking for. Good job @EdS.
-
Fabien Teulieres almost 4 yearsWorks like a charm! This should be the answer to the question.