认为创建一个自己绘制一些文本的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);
}
然而,我没有文本,甚至似乎都没有调用这些方法。这是怎么回事?
更新:到目前为止,由于这两个答案,我已经通过使用OnPaint
来实际调用SetStyle(ControlStyles.UserPaint, true)
了,我已经得到了它来绘制文本在new Rectangle(0, 0, Width, Height)
而不是Bounds
发送,在正确的位置。
我现在收到了文字,但是ProgressBar
已经消失了...而且要点ProgressBar
上的文字。知道如何解决这个问题吗?
答案 0 :(得分:12)
您可以覆盖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;
}
}
}
答案 1 :(得分:6)
我自己需要这样做,我想我会发布一个简化的解决方案示例,因为我找不到任何示例。如果您使用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 );
}
}
}
答案 2 :(得分:1)
您的问题是您传递Bounds
作为Rectangle参数。 Bounds包含控件的高度和宽度,这是您想要的,但它还包含控件的Top和Left属性,相对于父窗体,因此您的“Hello”在控件上偏移了多少控制在其父表单上偏移。
将Bounds
替换为new Rectangle(0, 0, this.Width, this.Height)
,您应该看到“Hello”。
答案 3 :(得分:1)
似乎如果你调用'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;
}
}
}
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的值发生变化并且需要时,您可能会看到此用户绘制的内容会闪烁重绘自己)。
答案 4 :(得分:0)
这是另一个解决方案以及其他人的建议。我将进度条控件子类化,以使其工作。为此我混合和匹配来自不同地方的代码。油漆事件可能更清洁,但这是你要做的;)
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)));
}
}
}