2012-03-21 80 views
1

我想開發一個標籤,以淡入顯示類型的方式顯示文本。我希望我需要處理這幅畫,以便在每次迭代時,基本上在右側添加幾個像素。但是,我碰到了一個障礙,無法獲得任何動畫效果。這是我到目前爲止有:繪製具有顯示/淡入效果的標籤文本

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對象離開油漆處理方法後,很快就熄滅了可用的狀態。)

回答

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繼承。

如果您希望標籤始終爲動畫,請將Tick事件中的this.Invalidate()呼叫移動到IF塊外部。

+0

謝謝!你爲什麼推薦在'Label'上使用'Panel'?我起初是這麼做的,但後來發現'Panel'隱藏了'Text'屬性,所以我最終只是從'Control'繼承而來。 'Panel'會給我帶來什麼? – 2012-03-21 15:58:06

+0

@GuthMD無關緊要。 '控制'將工作。 '面板'提供了邊界。 – LarsTech 2012-03-21 16:01:33

+0

你的解決方案有什麼比我結束了什麼?比如,使用'Invalidate'並總是在'OnPaint'處理函數中繪製更好?一個'Timer'比ThreadPool更受歡迎? 'TextRenderer'比剪輯集更適合'Graphics'嗎? – 2012-03-21 16:14:01

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

歡迎任何反饋。