C#: переопределение OnPaint на ProgressBar не работает?

думал, что это должно быть довольно легко создать 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. Есть идеи, как я могу это решить?

5 ответов


ваша проблема в том, что вы проходите в Bounds в качестве параметра прямоугольник. Границы содержатся и высота, и ширина вашего контроля, что вы хотите, но он также содержит Top и Left свойства управления, по отношению к родительской форме, так что ваше "здравствуйте" будет зачет по контрольной на сколько это смещение на родительской форме.

заменить Bounds с new Rectangle(0, 0, this.Width, this.Height) и вы должны увидеть ваш "Привет".


вы можете переопределить 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) ' не удалось вызвать стандартный метод OnPaint, реализованный для ProgressBar (используя base.OnPaint (e) не работает вообще). Самое странное, что даже если вы действительно создаете UserControl и пытаетесь нарисовать какой-то текст на индикаторе выполнения... кажется, это тоже не работает... Конечно, вы можете наклеить на него ярлык... но я полагаю, на самом деле это не то, чего вы хотели достичь.

ОК, кажется что мне удалось решить эту проблему. Это, хотя и немного сложно. Сначала нужно создать прозрачный элемент управления Label. Код ниже:

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

странная вещь с ProgressBar заключается в том, что он "перекрывает" элементы управления, которые размещаются на нем, поэтому необходимо отправить progressbar назад и вывести элемент управления label на передний план. На данный момент я не нашел более элегантного решения. Это работает, метка отображается на progressbar, фон элемента управления label прозрачен, поэтому я думаю, что он выглядит так, как вы хотели, чтобы он выглядел :)

Я могу поделиться своим примером кода, если вы хотите...

О, кстати. это странное поведение элемента управления ProgressBar, о котором я упоминал, отвечает за то, что невозможно использовать графический объект для рисования чего-либо на элементе управления, производном от ProgressBar. Текст (или все, что вы рисуете с помощью графического объекта) на самом деле рисуется, но... за управлением ProgressBar (если вы посмотрите внимательнее, вы можете увидеть, что этот пользователь нарисовал вещи, мерцающие, когда значение ProgressBar изменяется, и ему нужно перекрасить себя).


вот еще одно решение, наряду с предложениями других людей. Я подкласс progressbar управления, чтобы сделать эту работу. Я смешал и сопоставил коды из разных мест для этого. Событие paint может быть чище, но это для вас;)

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