C#:在ProgressBar上覆盖OnPaint不起作用?

认为创建一个自己绘制一些文本的ProgressBar应该很容易。 但是,我不太清楚这里发生了什么……

我添加了以下两个覆盖:

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

但是,我没有文本,甚至似乎都没有调用这些方法。 这里发生了什么?


更新:感谢到目前为止的两个答案,我已经通过使用SetStyle(ControlStyles.UserPaint, true)来实际调用OnPaint ,并且我已经通过发送new Rectangle(0, 0, Width, Height) SetStyle(ControlStyles.UserPaint, true)得到它来在正确的位置绘制文本new Rectangle(0, 0, Width, Height)而不是Bounds

我现在得到了文本,但是ProgressBar已经消失了…而且有点将文本放在ProgressBar之上。 知道怎么解决这个问题吗?

你的问题是你传入Bounds作为Rectangle参数。 Bounds包含控件的高度和宽度,这是您想要的,但它还包含控件的Top和Left属性,相对于父窗体,因此您的“Hello”在控件上偏移了多少控件在其父窗体上偏移。

new Rectangle(0, 0, this.Width, this.Height)替换Bounds ,你应该看到你的“Hello”。

您可以覆盖WndProc并捕获WmPaint消息。

下面的示例在其中心绘制进度条的Text属性。

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

我需要自己做这件事,我想我会发布一个简化的解决方案示例,因为我找不到任何例子。 如果您使用ProgressBarRenderer类,实际上非常简单:

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

看来如果你调用’SetStyle(ControlStyles.UserPaint,true)’,就无法调用为ProgressBar实现的标准OnPaint方法(使用base.OnPaint(e)根本不起作用)。 最奇怪的是,即使你实际上创建了一个UserControl,并尝试在进度条上绘制一些文字…它似乎也不起作用……当然你可以在它上面放置一个标签。 ..但我想这实际上并不是你想要实现的目标。

好吧,似乎我设法解决了这个问题。 虽然有点复杂。 首先,您需要创建一个透明的Label控件。 代码如下:

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

第二件事是创建UserControl,在其上放置一个ProgressBar(Dock = Fill) – 这将是我们将使用的控件,而不是标准的ProgressBar。 码:

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

ProgressBar的奇怪之处在于它“透支”了放置在它上面的控件,因此需要将progressbar发送回来,并将标签控件置于前面。 我目前还没有找到更优雅的解决方案。 这样做,标签显示在进度条上,标签控件的背景是透明的,所以我认为看起来你想要它看起来:)

如果你愿意,我可以分享我的示例代码……

哦,顺便问一下。 我提到的ProgressBar控件的这种奇怪的行为,导致无法使用Graphics对象在从ProgressBar派生的控件上绘制任何内容。 实际上正在绘制文本(或使用Graphics对象绘制的任何内容)但是……在ProgressBar控件后面(如果仔细观察,当ProgressBar的值发生变化时,您可能会看到此用户绘制的内容会闪烁,并且需要重绘自己)。

这是另一个解决方案以及其他人的建议。 我将进度条控件子类化以使其工作。 为此我混合和匹配来自不同地方的代码。 油漆事件可能更清洁,但这是你要做的;)

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