绘画标签文字与揭示/淡入效果

时间:2012-03-21 13:25:54

标签: .net winforms c#-4.0

我想开发一个标签,以淡入淡出的方式显示文字。我希望我需要处理这个绘画,以便在每次迭代时向右添加几个像素。但是,我遇到了障碍,无法使任何动画工作。这是我到目前为止所做的:

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. 如何让它在每次抽奖时更新到屏幕,而不仅仅是完成后的一个块?

  3. (注意:我也尝试在后台线程中绘画,但是一直保持ArgumentException,我认为因为Graphics对象在离开绘制处理程序方法后很快就会失去可用状态。)

2 个答案:

答案 0 :(得分:1)

尝试使用计时器:

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()调用移至IF块之外的Tick事件中。

答案 1 :(得分:0)

在敲开这个之后,我终于找到了解决方案,在这里:

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

欢迎任何反馈。