7

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通过 using实际调用了它SetStyle(ControlStyles.UserPaint, true),并且我已经通过发送new Rectangle(0, 0, Width, Height)而不是发送它来在正确的位置绘制文本Bounds

我现在确实收到了文字,但它ProgressBar已经消失了……重点是在ProgressBar. 知道如何解决这个问题吗?

4

5 回答 5

14

您可以覆盖 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;
        }
    }
}
于 2010-05-21T10:11:54.700 回答
7

我需要自己做这件事,我想我会发布一个简化的解决方案示例,因为我找不到任何示例。如果您使用 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 ); 
        }
    }
}
于 2010-01-15T07:51:31.600 回答
1

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

替换Boundsnew Rectangle(0, 0, this.Width, this.Height),您应该会看到您的“Hello”。

于 2009-10-04T21:12:03.790 回答
1

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

好的,看来我已经设法解决了这个问题。虽然有点复杂。首先,您需要创建一个透明的 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 控件的这种奇怪行为导致无法使用 Graphics 对象在派生自 ProgressBar 的控件上绘制任何内容。文本(或您使用 Graphics 对象绘制的任何内容)实际上正在绘制,但是......在 ProgressBar 控件后面(如果您仔细观察,您可能会看到该用户绘制的东西在 ProgressBar 的值发生变化时闪烁并且它需要重绘自己)。

于 2009-10-05T12:53:11.580 回答
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)));
         }
       }
    }
于 2010-07-17T04:58:17.193 回答