文本跑马灯特效控件----------WinForm控件开发系列

Posted tlmbem

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了文本跑马灯特效控件----------WinForm控件开发系列相关的知识,希望对你有一定的参考价值。

技术图片

控件可以添加文本段列表,每段文本可以设置出现动画和文字渐变颜色,还可以让指定某一段文本重复播放指定的次数。

  /// <summary>
  /// 文本跑马灯特效控件
  /// </summary>
  [ToolboxItem(true)]
  [DefaultProperty("Items")]
  [Description("文本跑马灯特效控件")]
  public partial class TextCarouselExt : Control
  
    public delegate void IndexEventHandler(object sender, IndexEventArgs e);

    #region

    private event IndexEventHandler indexChanged;
    /// <summary>
    /// 当前正在播放文本选项索引更改事件
    /// </summary>
    [Description("当前正在播放文本选项索引更改事件")]
    public event IndexEventHandler IndexChanged
    
      add  this.indexChanged += value; 
      remove  this.indexChanged -= value; 
    

    private bool active = false;
    /// <summary>
    /// 文本轮播激活状态
    /// </summary>
    [DefaultValue(false)]
    [Description("文本轮播激活状态")]
    public bool Active
    
      get  return this.active; 
      set
      
        if (this.active == value)
          return;
        this.active = value;
        this.intervalTimer.Enabled = value;
      
    

    private int intervalTime = 100;
    /// <summary>
    /// 文本选项轮播的时间间隔
    /// </summary>
    [DefaultValue(100)]
    [Description("文本选项轮播的时间间隔(默认100毫秒)")]
    public int IntervalTime
    
      get  return this.intervalTime; 
      set
      
        if (this.intervalTime == value)
          return;
        this.intervalTime = value;
      
    

    int currentIndex = -1;
    /// <summary>
    ///当前正在播放文本选项索引 
    /// </summary>
    [Browsable(false)]
    [DefaultValue(-1)]
    [Description("当前正在播放文本选项索引")]
    public int CurrentIndex
    
      get  return this.currentIndex; 
      set
      
        if (this.currentIndex == value)
          return;
        this.currentIndex = value;
        if (this.indexChanged != null)
          this.indexChanged(this, new IndexEventArgs()  Index = this.currentIndex, Item = this.Items[this.currentIndex] );
      
    

    private TextItemCollection textItemCollection;
    /// <summary>
    /// 文本选项集合
    /// </summary>
    [DefaultValue(null)]
    [Description("文本选项集合")]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public TextItemCollection Items
    
      get
      
        if (this.textItemCollection == null)
          this.textItemCollection = new TextItemCollection(this);
        return this.textItemCollection;
      
    

    [Editor(typeof(ColorEditorExt), typeof(System.Drawing.Design.UITypeEditor))]
    public override Color BackColor
    
      get
      
        return base.BackColor;
      
      set
      
        base.BackColor = value;
      
    

    protected override Size DefaultSize
    
      get
      
        return new Size(300, 60);
      
    

    /// <summary>
    /// 文本选项轮播的时间间隔累计(-1为动画正在切换中)(-2出现后停留中)
    /// </summary>
    private int intervalTimeValue = 0;
    /// <summary>
    /// 文本选项播放时间间隔定时器
    /// </summary>
    private Timer intervalTimer;
    /// <summary>
    /// 文本选项出现后停留时间累计
    /// </summary>
    private int remainTimeValue = 0;
    /// <summary>
    /// 动画播放定时器
    /// </summary>
    private AnimationTimer animationTimer;

    #endregion

    public TextCarouselExt()
    
      SetStyle(ControlStyles.UserPaint, true);
      SetStyle(ControlStyles.AllPaintingInWmPaint, true);
      SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
      SetStyle(ControlStyles.ResizeRedraw, true);
      SetStyle(ControlStyles.SupportsTransparentBackColor, true);

      InitializeComponent();

      this.InitializeTextRectangles();

      this.intervalTimer = new Timer();
      this.intervalTimer.Interval = 50;
      this.intervalTimer.Tick += new EventHandler(this.intervalTimer_Tick);

      this.animationTimer = new AnimationTimer(this, new AnimationOptions());
      this.animationTimer.AnimationIng += new AnimationTimer.AnimationHandel(this.animationTimer_AnimationIng);
      this.animationTimer.AnimationEnding += new AnimationTimer.AnimationHandel(this.animationTimer_AnimationEnding);

    

    #region 重写

    protected override void OnPaint(PaintEventArgs e)
    
      base.OnPaint(e);

      if (this.Items.Count < 0 || this.CurrentIndex < 0)
        return;
      Graphics g = e.Graphics;
      TextItem textItem = this.Items[this.CurrentIndex];

      #region Scroll
      if (textItem.Way == AnimationWay.Scroll)
      
        if (textItem.TextColorItems.Count < 1)
        
          SolidBrush text_sb = new SolidBrush(textItem.TextColor);
          g.DrawString(textItem.Text, textItem.TextFont, text_sb, textItem.current_rectf);
          text_sb.Dispose();
        
        else
        
          int len = Math.Max(textItem.TextColorItems.Count, 2);
          Color[] colors = new Color[len];
          float[] interval = new float[len];

          for (int i = 0; i < textItem.TextColorItems.Count; i++)
          
            colors[i] = textItem.TextColorItems[i].ShadeColor;
            interval[i] = textItem.TextColorItems[i].Shade;
          
          if (textItem.TextColorItems.Count == 1)
          
            colors[1] = textItem.TextColorItems[0].ShadeColor;
            interval[1] = textItem.TextColorItems[0].Shade;
          
          //Positions开始值必须为0,结束值必须为1
          interval[0] = 0.0f;
          interval[textItem.TextColorItems.Count - 1] = 1.0f;

          LinearGradientBrush text_lgb = new LinearGradientBrush(textItem.current_rectf, Color.Transparent, Color.Transparent, textItem.ShadeAngle);
          text_lgb.InterpolationColors = new ColorBlend()  Colors = colors, Positions = interval ;
          g.DrawString(textItem.Text, textItem.TextFont, text_lgb, textItem.current_rectf);
          text_lgb.Dispose();
        
      
      #endregion
      #region Speed SpeedSpringback
      else if (textItem.Way == AnimationWay.Speed || textItem.Way == AnimationWay.SpeedSpringback)
      
        if (textItem.TextColorItems.Count < 1)
        
          SolidBrush text_sb = new SolidBrush(textItem.TextColor);
          for (int i = 0; i < textItem.TextChar.Length; i++)
          
            g.DrawString(textItem.TextChar[i], textItem.TextFont, text_sb, textItem.current_char_rectf[i]);
          
          text_sb.Dispose();
        
        else
        
          int len = Math.Max(textItem.TextColorItems.Count, 2);
          Color[] colors = new Color[len];
          float[] interval = new float[len];
          RectangleF text_lgb_rectf = new RectangleF(textItem.current_char_rectf[0].X, textItem.current_char_rectf[0].Y, textItem.current_char_rectf[textItem.TextChar.Length - 1].Right, textItem.current_char_rectf[0].Height);
          LinearGradientBrush text_lgb = new LinearGradientBrush(text_lgb_rectf, Color.Transparent, Color.Transparent, textItem.ShadeAngle);

          for (int k = 0; k < textItem.TextChar.Length; k++)
          
            for (int i = 0; i < textItem.TextColorItems.Count; i++)
            
              colors[i] = textItem.TextColorItems[i].ShadeColor;
              interval[i] = textItem.TextColorItems[i].Shade;
            
            if (textItem.TextColorItems.Count == 1)
            
              colors[1] = textItem.TextColorItems[0].ShadeColor;
              interval[1] = textItem.TextColorItems[0].Shade;
            
            //Positions开始值必须为0,结束值必须为1
            interval[0] = 0.0f;
            interval[textItem.TextColorItems.Count - 1] = 1.0f;
            text_lgb.InterpolationColors = new ColorBlend()  Colors = colors, Positions = interval ;

            g.DrawString(textItem.TextChar[k], textItem.TextFont, text_lgb, textItem.current_char_rectf[k]);
          
          text_lgb.Dispose();
        
      #endregion

      
    

    protected override void OnResize(EventArgs e)
    
      base.OnResize(e);

      this.InitializeTextRectangles();
    

    #endregion

    #region 动画

    /// <summary>
    /// 动画开始事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void intervalTimer_Tick(object sender, EventArgs e)
    
      #region 第一步检查动画是否为进行中
      if (this.intervalTimeValue == -1)//动画进行中
        return;
      #endregion

      #region 第二步检查文本是否为动画完成后停留中
      if (this.intervalTimeValue == -2)//停留中
      
        this.remainTimeValue += this.intervalTimer.Interval;
        if (this.remainTimeValue < this.Items[this.CurrentIndex].RemainTime * 1000)
        
          return;
        
        this.remainTimeValue = 0;
        this.intervalTimeValue = 0;
      
      #endregion

      #region 第三步检查是否要进行重复播放
      if (this.CurrentIndex > -1)
      
        this.Items[this.CurrentIndex].CurrentRepetition += 1;
        if (this.Items[this.CurrentIndex].CurrentRepetition <= this.Items[this.CurrentIndex].Repetition)
        
          goto DH;
        
        else
        
          this.Items[this.CurrentIndex].CurrentRepetition = 0;
        
      
      #endregion

      #region  第四步检查下一个文本选项是否禁用,禁用则跳到下下一个文本选项
      int index = this.CurrentIndex + 1;
      if (index > this.Items.Count - 1)
      
        index -= this.Items.Count;
      
      if (!this.Items[index].Enabled)
      
        this.CurrentIndex++;
        if (this.CurrentIndex > this.Items.Count - 1)
        
          this.CurrentIndex -= this.Items.Count;
        
      
      #endregion

      #region  第五步检查是否是时间进入下一个文本选项动画播放
      this.intervalTimeValue += this.intervalTimer.Interval;
      if (this.intervalTimeValue < this.IntervalTime)
      
        return;
      

      this.intervalTimeValue = -1;

      this.CurrentIndex++;
      if (this.CurrentIndex > this.Items.Count - 1)
      
        this.CurrentIndex -= this.Items.Count;
      
      #endregion

      #region 播放动画
    DH:
      TextItem textItem = this.Items[this.CurrentIndex];
      this.animationTimer.Options.Data = textItem;

      if (textItem.Way == AnimationWay.Scroll)
      
        this.animationTimer.AT = AnimationType.UniformMotion;
        this.animationTimer.Options.Power = 3;
        this.animationTimer.Options.AllTime = 100f * (textItem.Orientation == AnimationOrientation.Right ? (float)textItem.prev_rectf.Width : (float)textItem.prev_rectf.Height);//动画时间由文字长度决定,速度为每个像素使用50毫秒
      
      else if (textItem.Way == AnimationWay.Speed)
      
        this.animationTimer.AT = AnimationType.EaseOut;
        this.animationTimer.Options.AllTime = textItem.TextChar.Length * 100;
        this.animationTimer.Options.AllTime = 1000d;
      
      else if (textItem.Way == AnimationWay.SpeedSpringback)
      
        this.animationTimer.AT = AnimationType.BackOut;
        this.animationTimer.Options.Power = 1;
        this.animationTimer.Options.AllTime = 1000d;
      

      this.animationTimer.Start(true, 0);
      #endregion
    

    /// <summary>
    /// 动画进行中事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void animationTimer_AnimationIng(object sender, AnimationEventArgs e)
    
      TextItem textItem = (TextItem)e.Data;

      if (textItem.Way == AnimationWay.Scroll)
      
        float x = textItem.prev_rectf.X;
        float y = textItem.prev_rectf.Y;
        if (textItem.Orientation == AnimationOrientation.Top || textItem.Orientation == AnimationOrientation.Bottom)
        
          y = textItem.prev_rectf.Y + (float)((textItem.target_rectf.Y - textItem.prev_rectf.Y) * e.progressTime);
        
        else if (textItem.Orientation == AnimationOrientation.Right)
        
          x = textItem.prev_rectf.X + (float)((textItem.target_rectf.X - textItem.prev_rectf.X) * e.progressTime);
        
        textItem.current_rectf = new RectangleF(x, y, textItem.prev_rectf.Width, textItem.prev_rectf.Height);
      
      else if (textItem.Way == AnimationWay.Speed)
      
        for (int i = 0; i < textItem.TextChar.Length; i++)
        
          float x = textItem.prev_char_rectf[i].X;
          float y = textItem.prev_char_rectf[i].Y;
          if (textItem.Orientation == AnimationOrientation.Top || textItem.Orientation == AnimationOrientation.Bottom)
          
            y = textItem.prev_char_rectf[i].Y + (float)((textItem.target_char_rectf[i].Y - textItem.prev_char_rectf[i].Y) * e.progressTime);
          
          else if (textItem.Orientation == AnimationOrientation.Right)
          
            x = textItem.prev_char_rectf[i].X + (float)((textItem.target_char_rectf[i].X - textItem.prev_char_rectf[i].X) * e.progressTime);
          
          textItem.current_char_rectf[i] = new RectangleF(x, y, textItem.prev_char_rectf[i].Width, textItem.prev_char_rectf[i].Height);
        
      
      else if (textItem.Way == AnimationWay.SpeedSpringback)
      
        for (int i = 0; i < textItem.TextChar.Length; i++)
        
          float x = textItem.prev_char_rectf[i].X;
          float y = textItem.prev_char_rectf[i].Y;
          if (textItem.Orientation == AnimationOrientation.Top || textItem.Orientation == AnimationOrientation.Bottom)
          
            y = textItem.prev_char_rectf[i].Y + (float)((textItem.target_char_rectf[i].Y - textItem.prev_char_rectf[i].Y) * e.progressTime);
          
          else if (textItem.Orientation == AnimationOrientation.Right)
          
            x = textItem.prev_char_rectf[i].X + (float)((textItem.target_char_rectf[i].X - textItem.prev_char_rectf[i].X) * e.progressTime);
          
          textItem.current_char_rectf[i] = new RectangleF(x, y, textItem.prev_char_rectf[i].Width, textItem.prev_char_rectf[i].Height);
        
      

      this.Invalidate();
    

    /// <summary>
    /// 动画结束时事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void animationTimer_AnimationEnding(object sender, AnimationEventArgs e)
    
      this.intervalTimeValue = -2;
    

    #endregion

    #region 私有

    /// <summary>
    /// 初始化播放前每一段文本rect
    /// </summary>
    private void InitializeTextRectangles()
    
      for (int i = 0; i < this.Items.Count; i++)
      
        this.InitializeTextRectangle(this.Items[i]);
      
    

    /// <summary>
    /// 初始化文本rect
    /// </summary>
    /// <param name="textItem"></param>
    private void InitializeTextRectangle(TextItem textItem)
    
      Rectangle rect = this.ClientRectangle;
      Size text_size = TextRenderer.MeasureText(textItem.Text, textItem.TextFont, new Size(), TextFormatFlags.ExternalLeading);
      int len = textItem.Text.Length;
      int avg_w = len == 0 ? 0 : text_size.Width / len;

      #region  Scroll
      if (textItem.Way == AnimationWay.Scroll)
      
        float x = 0;
        float y = 0;
        float target_x = 0;
        float target_y = 0;
        if (textItem.Orientation == AnimationOrientation.Top)
        
          x = rect.X;
          if (textItem.TextCenter)
          
            x = rect.X + (rect.Width - text_size.Width) / 2;
            if (x < rect.X)
              x = rect.X;
          
          y = rect.Y - text_size.Height;

          target_x = x;
          target_y = rect.Bottom + text_size.Height;
        
        else if (textItem.Orientation == AnimationOrientation.Bottom)
        
          x = rect.X;
          if (textItem.TextCenter)
          
            x = rect.X + (rect.Width - text_size.Width) / 2;
            if (x < rect.X)
              x = rect.X;
          
          y = rect.Bottom;

          target_x = x;
          target_y = rect.Y - text_size.Height;
        
        else if (textItem.Orientation == AnimationOrientation.Right)
        
          x = rect.Right;
          y = (rect.Height - text_size.Height) / 2;

          target_x = rect.X - text_size.Width;
          target_y = y;
        

        textItem.prev_rectf = new RectangleF(x, y, text_size.Width, text_size.Height);
        textItem.current_rectf = textItem.prev_rectf;
        textItem.target_rectf = new RectangleF(target_x, target_y, text_size.Width, text_size.Height);
      
      #endregion
      #region Speed
      else if (textItem.Way == AnimationWay.Speed)
      
        textItem.TextCharAvgWidth = text_size.Width / len;

        textItem.TextChar = new string[len];
        textItem.prev_char_rectf = new RectangleF[len];
        textItem.current_char_rectf = new RectangleF[len];
        textItem.target_char_rectf = new RectangleF[len];
        for (int i = 0; i < len; i++)
        
          float x = 0;
          float y = 0;
          float target_x = 0;
          float target_y = 0;
          if (textItem.Orientation == AnimationOrientation.Top)
          
            float tmp_x = rect.X + i * avg_w;
            x = tmp_x;
            if (textItem.TextCenter)
            
              x = tmp_x + (rect.Width - text_size.Width) / 2;
              if (x < tmp_x)
                x = tmp_x;
            
            y = rect.Y - i * text_size.Height;

            target_x = x;
            target_y = (rect.Height - text_size.Height) / 2;
          
          else if (textItem.Orientation == AnimationOrientation.Bottom)
          
            float tmp_x = rect.X + i * avg_w;
            x = tmp_x;
            if (textItem.TextCenter)
            
              x = tmp_x + (rect.Width - text_size.Width) / 2;
              if (x < tmp_x)
                x = tmp_x;
            
            y = rect.Bottom + i * text_size.Height;

            target_x = x;
            target_y = (rect.Height - text_size.Height) / 2;
          
          else if (textItem.Orientation == AnimationOrientation.Right)
          
            x = rect.Right + i * rect.Width;
            y = (rect.Height - text_size.Height) / 2;

            target_x = rect.X + i * avg_w;
            target_y = y;
          

          textItem.TextChar[i] = textItem.Text.Substring(i, 1);
          textItem.prev_char_rectf[i] = new RectangleF(x, y, avg_w, text_size.Height);
          textItem.current_char_rectf[i] = textItem.prev_char_rectf[i];
          textItem.target_char_rectf[i] = new RectangleF(target_x, target_y, avg_w, text_size.Height);
        
      
      #endregion
      #region SpeedSpringback
      else if (textItem.Way == AnimationWay.SpeedSpringback)
      
        textItem.TextCharAvgWidth = text_size.Width / len;

        textItem.TextChar = new string[len];
        textItem.prev_char_rectf = new RectangleF[len];
        textItem.current_char_rectf = new RectangleF[len];
        textItem.target_char_rectf = new RectangleF[len];
        for (int i = 0; i < len; i++)
        
          float x = 0;
          float y = 0;
          float target_x = 0;
          float target_y = 0;
          if (textItem.Orientation == AnimationOrientation.Top)
          
            float tmp_x = rect.X + i * avg_w;
            x = tmp_x;
            if (textItem.TextCenter)
            
              x = tmp_x + (rect.Width - text_size.Width) / 2;
              if (x < tmp_x)
                x = tmp_x;
            
            y = rect.Y - text_size.Height - text_size.Height - text_size.Height / 2;

            target_x = x;
            target_y = (rect.Height - text_size.Height) / 2;
          
          else if (textItem.Orientation == AnimationOrientation.Bottom)
          
            float tmp_x = rect.X + i * avg_w;
            x = tmp_x;
            if (textItem.TextCenter)
            
              x = tmp_x + (rect.Width - text_size.Width) / 2;
              if (x < tmp_x)
                x = tmp_x;
            
            y = rect.Bottom + text_size.Height + text_size.Height / 2;

            target_x = x;
            target_y = (rect.Height - text_size.Height) / 2;
          
          else if (textItem.Orientation == AnimationOrientation.Right)
          
            x = rect.Right + i * avg_w * 3;
            y = (rect.Height - text_size.Height) / 2;

            target_x = rect.X + i * avg_w;
            target_y = y;
          

          textItem.TextChar[i] = textItem.Text.Substring(i, 1);
          textItem.prev_char_rectf[i] = new RectangleF(x, y, avg_w, text_size.Height);
          textItem.current_char_rectf[i] = textItem.prev_char_rectf[i];
          textItem.target_char_rectf[i] = new RectangleF(target_x, target_y, avg_w, text_size.Height);
        
      
      #endregion

    

    #endregion

    protected override void Dispose(bool disposing)
    
      if (disposing && (components != null))
      
        components.Dispose();
        if (this.intervalTimer != null)
        
          this.intervalTimer.Dispose();
        
        if (this.animationTimer != null)
        
          this.animationTimer.Dispose();
        
      
      base.Dispose(disposing);
    

    /// <summary>
    /// 文本选项集合
    /// </summary>
    [Description("文本选项集合")]
    [Editor(typeof(CollectionEditorExt), typeof(UITypeEditor))]
    public sealed class TextItemCollection : IList, ICollection, IEnumerable
    
      private ArrayList textItemList = new ArrayList();
      private TextCarouselExt owner;

      public TextItemCollection(TextCarouselExt owner)
      
        this.owner = owner;
      

      #region IEnumerable

      public IEnumerator GetEnumerator()
      
        TextItem[] listArray = new TextItem[this.textItemList.Count];
        for (int index = 0; index < listArray.Length; ++index)
          listArray[index] = (TextItem)this.textItemList[index];
        return listArray.GetEnumerator();
      

      #endregion

      #region ICollection

      public void CopyTo(Array array, int index)
      
        for (int i = 0; i < this.Count; i++)
          array.SetValue(this.textItemList[i], i + index);
      

      public int Count
      
        get
        
          return this.textItemList.Count;
        
      

      public bool IsSynchronized
      
        get
        
          return false;
        
      

      public object SyncRoot
      
        get
        
          return (object)this;
        
      

      #endregion

      #region IList

      public int Add(object value)
      
        TextItem textItem = (TextItem)value;
        this.textItemList.Add(textItem);
        this.owner.InitializeTextRectangle(textItem);
        this.owner.Invalidate();
        return this.Count - 1;
      

      public void Clear()
      
        this.textItemList.Clear();
        this.owner.Invalidate();
      

      public bool Contains(object value)
      
        return this.IndexOf(value) != -1;
      

      public int IndexOf(object value)
      
        return this.textItemList.IndexOf(value);
      

      public void Insert(int index, object value)
      
        throw new NotImplementedException();
      

      public bool IsFixedSize
      
        get  return false; 
      

      public bool IsReadOnly
      
        get  return false; 
      

      public void Remove(object value)
      
        if (!(value is TextItem))
          return;
        this.textItemList.Remove((TextItem)value);
        this.owner.Invalidate();
      

      public void RemoveAt(int index)
      
        this.textItemList.RemoveAt(index);
        this.owner.Invalidate();
      

      public TextItem this[int index]
      
        get
        
          return (TextItem)this.textItemList[index];
        
        set
        
          this.textItemList[index] = (TextItem)value;
          this.owner.InitializeTextRectangle((TextItem)value);
          this.owner.Invalidate();
        
      

      object IList.this[int index]
      
        get
        
          return (object)this.textItemList[index];
        
        set
        
          this.textItemList[index] = (TextItem)value;
          this.owner.InitializeTextRectangle((TextItem)value);
          this.owner.Invalidate();
        
      

      #endregion

    

    /// <summary>
    /// 渐变颜色选项集合
    /// </summary>
    [Description("渐变颜色选项集合")]
    [Editor(typeof(CollectionEditorExt), typeof(UITypeEditor))]
    public sealed class ShadeColorItemCollection : IList, ICollection, IEnumerable
    
      private ArrayList shadeColorItemList = new ArrayList();

      public ShadeColorItemCollection()
      

      

      #region IEnumerable

      public IEnumerator GetEnumerator()
      
        ShadeColorItem[] listArray = new ShadeColorItem[this.shadeColorItemList.Count];
        for (int index = 0; index < listArray.Length; ++index)
          listArray[index] = (ShadeColorItem)this.shadeColorItemList[index];
        return listArray.GetEnumerator();
      

      #endregion

      #region ICollection

      public void CopyTo(Array array, int index)
      
        for (int i = 0; i < this.Count; i++)
          array.SetValue(this.shadeColorItemList[i], i + index);
      

      public int Count
      
        get
        
          return this.shadeColorItemList.Count;
        
      

      public bool IsSynchronized
      
        get
        
          return false;
        
      

      public object SyncRoot
      
        get
        
          return (object)this;
        
      

      #endregion

      #region IList

      public int Add(object value)
      
        ShadeColorItem textItem = (ShadeColorItem)value;
        this.shadeColorItemList.Add(textItem);
        return this.Count - 1;
      

      public void Clear()
      
        this.shadeColorItemList.Clear();
      

      public bool Contains(object value)
      
        return this.IndexOf(value) != -1;
      

      public int IndexOf(object value)
      
        return this.shadeColorItemList.IndexOf(value);
      

      public void Insert(int index, object value)
      
        throw new NotImplementedException();
      

      public bool IsFixedSize
      
        get  return false; 
      

      public bool IsReadOnly
      
        get  return false; 
      

      public void Remove(object value)
      
        if (!(value is ShadeColorItem))
          return;
        this.shadeColorItemList.Remove((ShadeColorItem)value);
      

      public void RemoveAt(int index)
      
        this.shadeColorItemList.RemoveAt(index);
      

      public ShadeColorItem this[int index]
      
        get
        
          return (ShadeColorItem)this.shadeColorItemList[index];
        
        set
        
          this.shadeColorItemList[index] = (ShadeColorItem)value;
        
      

      object IList.this[int index]
      
        get
        
          return (object)this.shadeColorItemList[index];
        
        set
        
          this.shadeColorItemList[index] = (ShadeColorItem)value;
        
      

      #endregion

    

    /// <summary>
    /// 文本选项
    /// </summary>
    [Description("文本选项")]
    public class TextItem
    

      private bool enabled = true;
      /// <summary>
      /// 文本选项是否参与轮播
      /// </summary>
      [DefaultValue(true)]
      [Description("文本选项是否参与轮播")]
      public bool Enabled
      
        get  return this.enabled; 
        set
        
          if (this.enabled == value)
            return;
          this.enabled = value;
        
      

      private AnimationWay way = AnimationWay.Scroll;
      /// <summary>
      /// 动画方式
      /// </summary>
      [DefaultValue(AnimationWay.Scroll)]
      [Description("动画方式")]
      public AnimationWay Way
      
        get  return this.way; 
        set
        
          if (this.way == value)
            return;
          this.way = value;
        
      

      private AnimationOrientation orientation = AnimationOrientation.Right;
      /// <summary>
      /// 动画出现方向
      /// </summary>
      [DefaultValue(AnimationOrientation.Right)]
      [Description("动画出现方向")]
      public AnimationOrientation Orientation
      
        get  return this.orientation; 
        set
        
          if (this.orientation == value)
            return;
          this.orientation = value;
        
      

      private int remainTime = 0;
      /// <summary>
      /// 文本选项出现后停留时间(默认0秒)
      /// </summary>
      [DefaultValue(0)]
      [Description("文本选项出现后停留时间(默认0秒)")]
      public int RemainTime
      
        get  return this.remainTime; 
        set
        
          if (this.remainTime == value || value < 0)
            return;
          this.remainTime = value;
        
      

      private int repetition = 1;
      /// <summary>
      /// 文本选项在单次循环轮播中重复次数(默认1次)
      /// </summary>
      [DefaultValue(1)]
      [Description("文本选项在单次循环轮播中重复次数(默认1次)")]
      public int Repetition
      
        get  return this.repetition; 
        set
        
          if (this.repetition == value || value < 1)
            return;
          this.repetition = value;
        
      

      private string text = "";
      /// <summary>
      /// 文本
      /// </summary>
      [DefaultValue("")]
      [Description("文本字体")]
      public string Text
      
        get  return this.text; 
        set
        
          if (this.text == value)
            return;
          this.text = value;
        
      

      private string[] textChar = null;
      /// <summary>
      /// 文本每个字符集合
      [Browsable(false)]
      [DefaultValue(null)]
      [Description("文本每个字符集合")]
      public string[] TextChar
      
        get  return this.textChar; 
        set
        
          this.textChar = value;
        
      

      private float textCharAvgWidth = 0f;
      /// <summary>
      /// 文本每个字符平均宽度
      [Browsable(false)]
      [DefaultValue(0f)]
      [Description("文本每个字符平均宽度")]
      public float TextCharAvgWidth
      
        get  return this.textCharAvgWidth; 
        set
        
          this.textCharAvgWidth = value;
        
      

      private bool textCenter = false;

      /// <summary>
      /// 文本是否水平居中(仅限垂直动画)
      /// </summary>
      [DefaultValue(false)]
      [Description("文本是否水平居中(仅限垂直动画)")]
      public bool TextCenter
      
        get  return this.textCenter; 
        set
        
          this.textCenter = value;
        
      

      private Font textFont = new Font("宋体", 20);
      /// <summary>
      /// 文本字体
      /// </summary>
      [DefaultValue(typeof(Font), "宋体, 20pt")]
      [Description("文本字体")]
      public Font TextFont
      
        get  return this.textFont; 
        set
        
          if (this.textFont == value)
            return;
          this.textFont = value;
        
      

      private Color textColor = Color.FromArgb(154, 205, 50);
      /// <summary>
      /// 文本颜色
      /// </summary>
      [DefaultValue(typeof(Color), "154, 205, 50")]
      [Description("文本颜色")]
      [Editor(typeof(ColorEditorExt), typeof(System.Drawing.Design.UITypeEditor))]
      public Color TextColor
      
        get  return this.textColor; 
        set
        
          if (this.textColor == value)
            return;
          this.textColor = value;
        
      

      private ShadeColorItemCollection shadeColorItemCollection;
      /// <summary>
      /// 渐变颜色选项集合
      /// </summary>
      [DefaultValue(null)]
      [Description("渐变颜色选项集合")]
      [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
      public ShadeColorItemCollection TextColorItems
      
        get
        
          if (this.shadeColorItemCollection == null)
            this.shadeColorItemCollection = new ShadeColorItemCollection();
          return this.shadeColorItemCollection;
        
      

      private float shadeAngle = 0f;
      /// <summary>
      /// 渐变方向线的角度(从 X 轴以顺时针角度计算)
      /// </summary>
      [DefaultValue(0f)]
      [Description("渐变方向线的角度(从 X 轴以顺时针角度计算)")]
      public float ShadeAngle
      
        get  return this.shadeAngle; 
        set
        
          if (this.shadeAngle == value || value < 0f || value > 360f)
            return;
          this.shadeAngle = value;
        
      

      private int currentRepetition = 0;
      /// <summary>
      /// 文本选项在单次循环轮播中当前的重复次数
      /// </summary>
      [Browsable(false)]
      [DefaultValue(0)]
      [Description("文本选项在单次循环轮播中当前的重复次数")]
      public int CurrentRepetition
      
        get  return this.currentRepetition; 
        set
        
          if (this.currentRepetition == value)
            return;
          this.currentRepetition = value;
        
      

      /// <summary>
      /// 运动前rectf
      /// </summary>
      [Browsable(false)]
      [Description("运动前rectf")]
      public RectangleF prev_rectf  get; set; 

      /// <summary>
      /// 目标rectf
      /// </summary>
      [Browsable(false)]
      [Description("目标rectf")]
      public RectangleF target_rectf  get; set; 

      /// <summary>
      /// 当前rectf
      /// </summary>
      [Browsable(false)]
      [Description("当前rectf")]
      public RectangleF current_rectf  get; set; 

      /// <summary>
      /// 运动前每个字的rectf
      /// </summary>
      [Browsable(false)]
      [Description("运动前rectf")]
      public RectangleF[] prev_char_rectf  get; set; 

      /// <summary>
      /// 目标每个字rectf
      /// </summary>
      [Browsable(false)]
      [Description("目标每个字rectf")]
      public RectangleF[] target_char_rectf  get; set; 

      /// <summary>
      /// 当前每个字的rectf
      /// </summary>
      [Browsable(false)]
      [Description("当前rectf")]
      public RectangleF[] current_char_rectf  get; set; 

    

    /// <summary>
    /// 渐变颜色选项
    /// </summary>
    [Description("渐变颜色选项")]
    public class ShadeColorItem
    

      private float shade = 0f;
      /// <summary>
      /// 渐变值0-1
      /// </summary>
      [DefaultValue(0f)]
      [Description("渐变值0-1")]
      public float Shade
      
        get  return this.shade; 
        set
        
          if (this.shade == value || value < 0 || value > 1)
            return;
          this.shade = value;
        
      

      private Color shadeColor = Color.Empty;
      /// <summary>
      /// 渐变值对应渐变颜色
      /// </summary>
      [DefaultValue(typeof(Color), "Empty")]
      [Description("渐变值对应渐变颜色")]
      [Editor(typeof(ColorEditorExt), typeof(System.Drawing.Design.UITypeEditor))]
      public Color ShadeColor
      
        get  return this.shadeColor; 
        set
        
          if (this.shadeColor == value)
            return;
          this.shadeColor = value;
        
      

    

    /// <summary>
    /// 动画方式
    /// </summary>
    [Description("动画方式")]
    public enum AnimationWay
    
      /// <summary>
      /// 普通滚动
      /// </summary>
      Scroll,
      /// <summary>
      /// 减速
      /// </summary>
      Speed,
      /// <summary>
      /// 减速回弹
      /// </summary>
      SpeedSpringback
    

    /// <summary>
    /// 动画出现方向
    /// </summary>
    [Description("控件滑块方向位置")]
    public enum AnimationOrientation
    
      /// <summary>
      ///从上边出现
      /// </summary>
      Top,
      /// <summary>
      /// 从下边出现
      /// </summary>
      Bottom,
      /// <summary>
      /// 从右边出现
      /// </summary>
      Right
    

    /// <summary>
    /// 当前正在播放文本选项索引更改事件参数
    /// </summary>
    [Description("当前正在播放文本选项索引更改事件参数")]
    public class IndexEventArgs : EventArgs
    
      /// <summary>
      /// 当前正在播放文本选项索引
      /// </summary>
      [Description("当前正在播放文本选项索引")]
      public float Index  get; set; 

      /// <summary>
      /// 当前正在播放文本选项
      /// </summary>
      [Description("当前正在播放文本选项")]
      public TextItem Item  get; set; 
    

  

 

源码下载:文本跑马灯特效控件.zip

以上是关于文本跑马灯特效控件----------WinForm控件开发系列的主要内容,如果未能解决你的问题,请参考以下文章

swing 跑马灯特效(不断动态前进的效果)

文字的跑马灯特效

Andriod开发之TextView控件应用以及跑马灯实现

Andriod开发之TextView控件应用以及跑马灯实现

jQuery+CSS3文字跑马灯特效

为 Xamarin.Forms 做个跑马灯控件