程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> Winform下無閃爍走馬燈效果實現

Winform下無閃爍走馬燈效果實現

編輯:關於C#

最近需要在Winform項目中實現一個走馬燈的效果,一開始用了一些辦法比如移動Label控件,效果總是不太好,移動文本時總有閃爍的現象。後來找了一個國外的開源控件,應用了一下,效果還不錯。仔細閱讀了一下代碼,發現還有一些地方值得改進,現把代碼以及改動說明貼出來,和大家分享。

控件出處:http://www.codeproject.com/KB/miscctrl/ScrollingTextControlArtic.aspx

我的改動:

1、DoubleBuffer 的設置

原代碼中用的是 this.SetStyle(ControlStyles.DoubleBuffer, true); 但一些網友反映這個標志在.net 2.0 以上版本無效。說句老實話,我也不是特別確信,MSDN上也沒有說明這一點。在我的.net 2.0 系統中,兩種設置的效果似乎沒有太多區別。在一個國外網站上找到他們的區別,下面是原文:

ControlStyles == CS

AllPaintingInWMPaint == APWMP

OptimizedDoubleBuffer = ODB

DoubleBuffer = DB

An earlier permutation of the design called for ODB to simply be a combinationof DB, APWMP and UserPaint. Through several design changes, the two control styles are nearly synonymous, but they still have differences. Now that we've broken that, we may considerun-deprecating CS.DB to retain . Here is a more complete summary of the current design:

Mechanism Side effects Other flags required to work Require APWMP? Notes ControlStyle

.DB none APWMP, UserPaint Yes We are considering NOT deprecating this flag because ODB isn't an exact replacement for DB. ControlStyle

.ODB none none No Works, but without APWMP set you'll buffer foreground and background separately and will still see flicker. Control

.DoubleBuffered sets CS.ODB, CS.APWMP none No Works for most mainstream buffering needs. Getter is peculiar in that it only checks CS.ODB.

I'm following up on the need for deprecating CS.DB and Control.DoubleBuffered's getter and will post here.

總之保險起見,還是判一下版本號,下面是判斷代碼

Version v = System.Environment.Version;
      if (v.Major < 2)
      {
        this.SetStyle(ControlStyles.DoubleBuffer, true);
      }
      else
      {
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
      }

2、刷新區域

原代碼中刷新區域是這樣設置的

private void Tick(object sender, EventArgs e)
    {
      //update rectangle to include where to paint for new position
      //lastKnownRect.X -= 10;
      //lastKnownRect.Width += 20;
      lastKnownRect.Inflate(10, 5);
      //create region based on updated rectangle
      Region updateRegion = new Region(lastKnownRect);
      //repaint the control
      Invalidate(updateRegion);
      Update();
    }

lastKnownRect是文字的整個區域,如果文字較長,這個刷新區域就會比較大,但實際上我們只需要刷新控件顯示范圍內的區域就可以了。

所以這裡改動如下:

//Controls the animation of the text.
    private void Tick(object sender, EventArgs e)
    {
      //update rectangle to include where to paint for new position
      //lastKnownRect.X -= 10;
      //lastKnownRect.Width += 20;
      lastKnownRect.Inflate(10, 5);
      //get the display rectangle
      RectangleF refreshRect = lastKnownRect;
      refreshRect.X = Math.Max(0, lastKnownRect.X);
      refreshRect.Width = Math.Min(lastKnownRect.Width + lastKnownRect.X, this.Width);
      refreshRect.Width = Math.Min(this.Width - lastKnownRect.X, refreshRect.Width);
      //create region based on updated rectangle
      //Region updateRegion = new Region(lastKnownRect);
      Region updateRegion = new Region(refreshRect);
      //repaint the control
      Invalidate(updateRegion);
      Update();
    }

2、修改Enabled屬性

當Enabled設置為false時,原控件依然會滾動,覺得還是不讓它滾動更好一些。

修改代碼如下:

[
    Browsable(true),
    CategoryAttribute("Behavior"),
    Description("Indicates whether the control is enabled")
    ]
    new public bool Enabled
    {
      set
      {
        timer.Enabled = value;
        base.Enabled = value;
      }
      get
      {
        return base.Enabled;
      }
    }

下面給出修改後完整的控件代碼,代碼原作者為jconwell,原始代碼見前面提到的控件出處

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Windows.Forms;
namespace ScrollingTextControl
{
  /**//// <summary>
  /// Summary description for ScrollingTextControl.
  /// </summary>
  [
  ToolboxBitmapAttribute(typeof(ScrollingTextControl.ScrollingText), "ScrollingText.bmp"),
  DefaultEvent("TextClicked")
  ]
  public class ScrollingText : System.Windows.Forms.Control
  {
    private Timer timer; // Timer for text animation.
    private string text = "Text"; // Scrolling text
    private float staticTextPos = 0; // The running x pos of the text
    private float yPos = 0; // The running y pos of the text
    private ScrollDirection scrollDirection = ScrollDirection.RightToLeft; // The direction the text will scroll
    private ScrollDirection currentDirection = ScrollDirection.LeftToRight; // Used for text bouncing
    private VerticleTextPosition verticleTextPosition = VerticleTextPosition.Center; // Where will the text be vertically placed
    private int scrollPixelDistance = 2; // How far the text scrolls per timer event
    private bool showBorder = true; // Show a border or not
    private bool stopScrollOnMouseOver = false; // Flag to stop the scroll if the user mouses over the text
    private bool scrollOn = true; // Internal flag to stop / start the scrolling of the text
    private Brush foregroundBrush = null; // Allow the user to set a custom Brush to the text Font
    private Brush backgroundBrush = null; // Allow the user to set a custom Brush to the background
    private Color borderColor = Color.Black; // Allow the user to set the color of the control border
    private RectangleF lastKnownRect; // The last known position of the text
    public ScrollingText()
    {
      // Setup default properties for ScrollingText control
      InitializeComponent();
      //This turns off internal double buffering of all custom GDI+ drawing
      Version v = System.Environment.Version;
      if (v.Major < 2)
      {
        this.SetStyle(ControlStyles.DoubleBuffer, true);
      }
      else
      {
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
      }
      this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
      this.SetStyle(ControlStyles.UserPaint, true);
      this.SetStyle(ControlStyles.ResizeRedraw, true);
      //setup the timer object
      timer = new Timer();
      timer.Interval = 25; //default timer interval
      timer.Enabled = true;
      timer.Tick += new EventHandler(Tick);
    }
    /**//// <summary>
    /// Clean up any resources being used.
    /// </summary>
    protected override void Dispose( bool disposing )
    {
      if( disposing )
      {
        //Make sure our brushes are cleaned up
        if (foregroundBrush != null)
          foregroundBrush.Dispose();
        //Make sure our brushes are cleaned up
        if (backgroundBrush != null)
          backgroundBrush.Dispose();
        //Make sure our timer is cleaned up
        if (timer != null)
          timer.Dispose();
      }
      base.Dispose( disposing );
    }
    Component Designer generated code#region Component Designer generated code
    /**//// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
      //ScrollingText
      this.Name = "ScrollingText";
      this.Size = new System.Drawing.Size(216, 40);
      this.Click += new System.EventHandler(this.ScrollingText_Click);
    }
    #endregion
    //Controls the animation of the text.
    private void Tick(object sender, EventArgs e)
    {
      //update rectangle to include where to paint for new position
      //lastKnownRect.X -= 10;
      //lastKnownRect.Width += 20;
      lastKnownRect.Inflate(10, 5);
      //get the display rectangle
      RectangleF refreshRect = lastKnownRect;
      refreshRect.X = Math.Max(0, lastKnownRect.X);
      refreshRect.Width = Math.Min(lastKnownRect.Width + lastKnownRect.X, this.Width);
      refreshRect.Width = Math.Min(this.Width - lastKnownRect.X, refreshRect.Width);
      //create region based on updated rectangle
      //Region updateRegion = new Region(lastKnownRect);
      Region updateRegion = new Region(refreshRect);
      //repaint the control
      Invalidate(updateRegion);
      Update();
    }
    //Paint the ScrollingTextCtrl.
    protected override void OnPaint(PaintEventArgs pe)
    {
      //Console.WriteLine(pe.ClipRectangle.X + ", " + pe.ClipRectangle.Y + ", " + pe.ClipRectangle.Width + ", " + pe.ClipRectangle.Height);
      //Paint the text to its new position
      DrawScrollingText(pe.Graphics);
      //pass on the graphics obj to the base Control class
      base.OnPaint(pe);
    }
    //Draw the scrolling text on the control
    public void DrawScrollingText(Graphics canvas)
    {
      canvas.SmoothingMode = SmoothingMode.HighQuality;
      canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
      //measure the size of the string for placement calculation
      SizeF stringSize = canvas.MeasureString(this.text, this.Font);
      //Calculate the begining x position of where to paint the text
      if (scrollOn)
      {
        CalcTextPosition(stringSize);
      }
      //Clear the control with user set BackColor
      if (backgroundBrush != null)
      {
        canvas.FillRectangle(backgroundBrush, 0, 0, this.ClientSize.Width, this.ClientSize.Height);
      }
      else
      {
        canvas.Clear(this.BackColor);
      }
      // Draw the border
      if (showBorder)
      {
        using (Pen borderPen = new Pen(borderColor))
          canvas.DrawRectangle(borderPen, 0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1);
      }
      // Draw the text string in the bitmap in memory
      if (foregroundBrush == null)
      {
        using (Brush tempForeBrush = new System.Drawing.SolidBrush(this.ForeColor))
          canvas.DrawString(this.text, this.Font, tempForeBrush, staticTextPos, yPos);
      }
      else
      {
        canvas.DrawString(this.text, this.Font, foregroundBrush, staticTextPos, yPos);
      }
      lastKnownRect = new RectangleF(staticTextPos, yPos, stringSize.Width, stringSize.Height);
      EnableTextLink(lastKnownRect);
    }
    private void CalcTextPosition(SizeF stringSize)
    {
      switch (scrollDirection)
      {
        case ScrollDirection.RightToLeft:
          if (staticTextPos < (-1 * (stringSize.Width)))
            staticTextPos = this.ClientSize.Width - 1;
          else
            staticTextPos -= scrollPixelDistance;
          break;
        case ScrollDirection.LeftToRight:
          if (staticTextPos > this.ClientSize.Width)
            staticTextPos = -1 * stringSize.Width;
          else
            staticTextPos += scrollPixelDistance;
          break;
        case ScrollDirection.Bouncing:
          if (currentDirection == ScrollDirection.RightToLeft)
          {
            if (staticTextPos < 0)
              currentDirection = ScrollDirection.LeftToRight;
            else
              staticTextPos -= scrollPixelDistance;
          }
          else if (currentDirection == ScrollDirection.LeftToRight)
          {
            if (staticTextPos > this.ClientSize.Width - stringSize.Width)
              currentDirection = ScrollDirection.RightToLeft;
            else
              staticTextPos += scrollPixelDistance;
          }
          break;
      }
      //Calculate the vertical position for the scrolling text
      switch (verticleTextPosition)
      {
        case VerticleTextPosition.Top:
          yPos = 2;
          break;
        case VerticleTextPosition.Center:
          yPos = (this.ClientSize.Height / 2) - (stringSize.Height / 2);
          break;
        case VerticleTextPosition.Botom:
          yPos = this.ClientSize.Height - stringSize.Height;
          break;
      }
    }
    Mouse over, text link logic#region Mouse over, text link logic
    private void EnableTextLink(RectangleF textRect)
    {
      Point curPt = this.PointToClient(Cursor.Position);
      //if (curPt.X > textRect.Left && curPt.X < textRect.Right
      //  && curPt.Y > textRect.Top && curPt.Y < textRect.Bottom)
      if (textRect.Contains(curPt))
      {
        //Stop the text of the user mouse's over the text
        if (stopScrollOnMouseOver)
          scrollOn = false;
        this.Cursor = Cursors.Hand;
      }
      else
      {
        //Make sure the text is scrolling if user's mouse is not over the text
        scrollOn = true;
        this.Cursor = Cursors.Default;
      }
    }
    private void ScrollingText_Click(object sender, System.EventArgs e)
    {
      //Trigger the text clicked event if the user clicks while the mouse
      //is over the text. This allows the text to act like a hyperlink
      if (this.Cursor == Cursors.Hand)
        OnTextClicked(this, new EventArgs());
    }
    public delegate void TextClickEventHandler(object sender, EventArgs args);
    public event TextClickEventHandler TextClicked;
    private void OnTextClicked(object sender, EventArgs args)
    {
      //Call the delegate
      if (TextClicked != null)
        TextClicked(sender, args);
    }
    #endregion
    Properties#region Properties
    [
    Browsable(true),
    CategoryAttribute("Scrolling Text"),
    Description("The timer interval that determines how often the control is repainted")
    ]
    public int TextScrollSpeed
    {
      set
      {
        timer.Interval = value;
      }
      get
      {
        return timer.Interval;
      }
    }
    [
    Browsable(true),
    CategoryAttribute("Scrolling Text"),
    Description("How many pixels will the text be moved per Paint")
    ]
    public int TextScrollDistance
    {
      set
      {
        scrollPixelDistance = value;
      }
      get
      {
        return scrollPixelDistance;
      }
    }
    [
    Browsable(true),
    CategoryAttribute("Scrolling Text"),
    Description("The text that will scroll accros the control")
    ]
    public string ScrollText
    {
      set
      {
        text = value;
        this.Invalidate();
        this.Update();
      }
      get
      {
        return text;
      }
    }
    [
    Browsable(true),
    CategoryAttribute("Scrolling Text"),
    Description("What direction the text will scroll: Left to Right, Right to Left, or Bouncing")
    ]
    public ScrollDirection ScrollDirection
    {
      set
      {
        scrollDirection = value;
      }
      get
      {
        return scrollDirection;
      }
    }
    [
    Browsable(true),
    CategoryAttribute("Scrolling Text"),
    Description("The verticle alignment of the text")
    ]
    public VerticleTextPosition VerticleTextPosition
    {
      set
      {
        verticleTextPosition = value;
      }
      get
      {
        return verticleTextPosition;
      }
    }
    [
    Browsable(true),
    CategoryAttribute("Scrolling Text"),
    Description("Turns the border on or off")
    ]
    public bool ShowBorder
    {
      set
      {
        showBorder = value;
      }
      get
      {
        return showBorder;
      }
    }
    [
    Browsable(true),
    CategoryAttribute("Scrolling Text"),
    Description("The color of the border")
    ]
    public Color BorderColor
    {
      set
      {
        borderColor = value;
      }
      get
      {
        return borderColor;
      }
    }
    [
    Browsable(true),
    CategoryAttribute("Scrolling Text"),
    Description("Determines if the text will stop scrolling if the user's mouse moves over the text")
    ]
    public bool StopScrollOnMouseOver
    {
      set
      {
        stopScrollOnMouseOver = value;
      }
      get
      {
        return stopScrollOnMouseOver;
      }
    }
    [
    Browsable(true),
    CategoryAttribute("Behavior"),
    Description("Indicates whether the control is enabled")
    ]
    new public bool Enabled
    {
      set
      {
        timer.Enabled = value;
        base.Enabled = value;
      }
      get
      {
        return base.Enabled;
      }
    }
    [
    Browsable(false)
    ]
    public Brush ForegroundBrush
    {
      set
      {
        foregroundBrush = value;
      }
      get
      {
        return foregroundBrush;
      }
    }
    [
    ReadOnly(true)
    ]
    public Brush BackgroundBrush
    {
      set
      {
        backgroundBrush = value;
      }
      get
      {
        return backgroundBrush;
      }
    }
    #endregion
  }
  public enum ScrollDirection
  {
    RightToLeft,
    LeftToRight,
    Bouncing
  }
  public enum VerticleTextPosition
  {
    Top,
    Center,
    Botom
  }
}

注意事項

如果要調整滾動速度,可以通過設置以下兩個屬性的值來實現

TextScrollSpeed 和 TextScrollDistance

TextScrollSpeed 其實是設置刷新頻率,單位是毫秒,這個值越小,滾動速度越快。但刷新頻率越高,CPU占用率越高。

TextScrollDistance 是指每次刷新移動的像素點,這個值越大,速度越快,但如果太大,文字滾動看起來就不是特別連貫。

所以在實際應用中我們需要同時調整這兩個值,以找到最佳的平衡點

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved