Окраска текста надписи с эффектом раскрытия / выцветания - PullRequest
1 голос
/ 21 марта 2012

Я хотел разработать лейбл, который бы отображал текст в режиме постепенного появления.Я ожидаю, что мне нужно обработать рисование, так что, по сути, еще несколько пикселей добавляются справа на каждой итерации.Тем не менее, я столкнулся с препятствием и не могу заставить работать анимацию.Вот что у меня есть:

class RevealLabel : System.Windows.Forms.Label
{
    protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
    {
        const int GradWidth = 7;
        Rectangle r = new Rectangle(e.ClipRectangle.X, e.ClipRectangle.Y,
                                    GradWidth, e.ClipRectangle.Height);
        GraphicsPath p = new GraphicsPath();
        using (SolidBrush bs = new SolidBrush(this.ForeColor))
        {
            using (LinearGradientBrush bg = new LinearGradientBrush(new Rectangle(0, 0, GradWidth, e.ClipRectangle.Height), 
                                                                    this.ForeColor, 
                                                                    this.BackColor, 
                                                                    LinearGradientMode.Horizontal))
            {
                for (int i = 0; i < e.ClipRectangle.Width; i += GradWidth)
                {
                    r.X = i;
                    p.AddString(this.Text, this.Font.FontFamily, (int)this.Font.Style, this.Font.Size, 
                                r, StringFormat.GenericDefault);
                    e.Graphics.FillPath(bg, p);
                    e.Graphics.Flush();
                    long TickStop = DateTime.Now.AddSeconds(0.2).Ticks;
                    while (DateTime.Now.Ticks < TickStop)
                    {
                        System.Windows.Forms.Application.DoEvents();
                    }
                    e.Graphics.FillPath(bs, p);
                }
            }
        }
        e.Graphics.Flush();
    }
}

Не уверен, что я на правильном пути, потому что это просто ужасный беспорядок.Беспорядок, который даже не выводится на экран постепенно, а скорее обрабатывает все в фоновом режиме, а затем обновляет экран только с конечным результатом.

Итак, мой вопрос двоякий:

  1. Как правильно отобразить область пикселей / прямоугольников, которую я буду добавлять справа на каждой итерации?

  2. Как получить ее для обновленияна экран на каждом розыгрыше, а не просто комок после завершения?

(Примечание: я также пытался рисовать в фоновом потоке, но продолжал получать ArgumentException, я думаю, потому что объект Graphics вышел из используемого состояния вскоре после выхода из метода обработчика рисования.)

Ответы [ 2 ]

1 голос
/ 21 марта 2012

Попробуйте использовать таймер:

public class RevealLabel : Label {
  private System.Windows.Forms.Timer revealTimer = new System.Windows.Forms.Timer();

  private int paintWidth = 0;
  private int paintIncrement = 7;

  public RevealLabel() {
    this.DoubleBuffered = true;
    revealTimer.Interval = 200;
    revealTimer.Tick += new EventHandler(revealTimer_Tick);
  }

  void revealTimer_Tick(object sender, EventArgs e) {
    paintWidth += paintIncrement;

    if (paintWidth > this.ClientSize.Width) {
      revealTimer.Enabled = false;
    } else {
      this.Invalidate();
    }
  }

  protected override void OnPaint(PaintEventArgs e) {
    if (revealTimer.Enabled) {
      e.Graphics.Clear(this.BackColor);
      Rectangle r = new Rectangle(0, 0, paintWidth, this.ClientSize.Height);
      TextRenderer.DrawText(e.Graphics, this.Text, this.Font, r, Color.Black, Color.Empty, TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
    } else {
      paintWidth = 0;
      revealTimer.Start();
    }
  }
}

Я бы, вероятно, унаследовал от Panel вместо Label, так как вы все равно делаете все рисование самостоятельно.

ЕслиВы хотите, чтобы метка всегда анимировалась, затем переместите вызов this.Invalidate() в событии Tick за пределы блока IF.

0 голосов
/ 21 марта 2012

После этого я наконец нашел решение:

public class RevealLabel : System.Windows.Forms.Control
{
    private const int GradWidth = 5;
    private const int DrawDelay = 20;
    private int lcBackgroundPaintThreadId;

    protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
    {
        e.Graphics.Clear(this.BackColor);
        if (this.DesignMode)
        {
            using (SolidBrush bs = new SolidBrush(this.ForeColor))
            {
                e.Graphics.DrawString(this.Text, this.Font, bs, 0, 0);
            }
        }
        else
        {
            System.Threading.ThreadPool.QueueUserWorkItem(QueuePaintStep, this.ClientRectangle.X);
        }
    }
    private void QueuePaintStep(object state)
    {
        lcBackgroundPaintThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
        System.Threading.Thread.Sleep(DrawDelay);
        if (System.Threading.Thread.CurrentThread.ManagedThreadId == lcBackgroundPaintThreadId)
        {
            int x = (int)state;
            if (x < this.ClientRectangle.Width)
            {
                Rectangle r = new Rectangle(x, this.ClientRectangle.Y, GradWidth, this.ClientRectangle.Height);
                if (System.Threading.Thread.CurrentThread.ManagedThreadId != lcBackgroundPaintThreadId) return;
                using (LinearGradientBrush bg = new LinearGradientBrush(new Rectangle(0, 0, GradWidth, r.Height),
                                                                        this.ForeColor,
                                                                        this.BackColor,
                                                                        LinearGradientMode.Horizontal))
                {
                    this.Invoke(AsyncPaintCommon, this, bg, r);
                }
                System.Threading.Thread.Sleep(DrawDelay);
                if (System.Threading.Thread.CurrentThread.ManagedThreadId != lcBackgroundPaintThreadId) return;
                using (SolidBrush bs = new SolidBrush(this.ForeColor))
                {
                    this.Invoke(AsyncPaintCommon, this, bs, r);
                }
                if (System.Threading.Thread.CurrentThread.ManagedThreadId != lcBackgroundPaintThreadId) return;
                QueuePaintStep(x + GradWidth);
            }
        }
    }
    private delegate void AsyncPaintDelegate(RevealLabel l, Brush b, Rectangle r);
    private static Delegate AsyncPaintCommon = new AsyncPaintDelegate((RevealLabel l, Brush b, Rectangle r) =>
    {
        using (Graphics g = l.CreateGraphics())
        {
            g.SetClip(r);
            g.DrawString(l.Text, l.Font, b, 0, 0);
        }
    });
}

Приветствую любые отзывы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...