C #: переопределение OnPaint на ProgressBar не работает? - PullRequest
6 голосов
/ 04 октября 2009

Я думал, что должно быть довольно легко создать 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 ]

12 голосов
/ 21 мая 2010

Вы можете переопределить 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;
        }
    }
}
6 голосов
/ 15 января 2010

Мне нужно было сделать это самому, и я подумал, что выложу упрощенный пример своего решения, так как я не смог найти никаких примеров. На самом деле это довольно просто, если вы используете класс 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 ); 
        }
    }
}
1 голос
/ 05 октября 2009

Кажется, что если вы вызываете SetStyle (ControlStyles.UserPaint, true), стандартный метод OnPaint, реализованный для ProgressBar, не может быть вызван (использование base.OnPaint (e) вообще не работает). Самое странное, что даже если вы на самом деле создаете UserControl и пытаетесь нарисовать какой-то текст на индикаторе выполнения ... он, похоже, тоже не работает ... Конечно, вы можете разместить над ним метку. ... но я полагаю, что это не то, чего вы хотели достичь.

Хорошо, похоже, мне удалось решить эту проблему. Хотя это немного сложно. Сначала вам нужно создать прозрачный элемент управления Label. Код ниже: </p> <pre><code>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. Код: </p> <pre><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, о котором я упоминал, является причиной того, что невозможно использовать объект Graphics для рисования чего-либо на элементе управления, производном от ProgressBar. Текст (или что-то, что вы рисуете с использованием объекта Graphics) на самом деле рисуется, но ... за элементом управления ProgressBar (если вы посмотрите поближе, вы можете увидеть, что этот пользователь рисует вещи, мерцающие, когда изменяется значение ProgressBar, и ему нужно перекрасить себя).

1 голос
/ 05 октября 2009

Ваша проблема в том, что вы передаете Bounds в качестве параметра Rectangle. Bounds содержит Height и Width вашего элемента управления, что вам и нужно, но также содержит свойства Top и Left вашего элемента управления относительно родительской формы, поэтому ваш «Hello» смещается на элемент управления, насколько бы элемент управления смещен в своей родительской форме.

Замените Bounds на new Rectangle(0, 0, this.Width, this.Height), и вы должны увидеть свой "Hello".

0 голосов
/ 17 июля 2010

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

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