/// <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; } } protected override Size DefaultSize { get { return new Size(300, 60); } } /// <summary> /// 文本选项轮播的时间间隔累计(-1为动画正在切换中) /// </summary> private int intervalTimeValue = 0; /// <summary> /// 文本选项播放时间间隔定时器 /// </summary> private Timer intervalTimer; /// <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); } /// <summary> /// 动画开始事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void intervalTimer_Tick(object sender, EventArgs e) { if (this.intervalTimeValue == -1)//动画进行中 return; this.intervalTimeValue += this.intervalTimer.Interval; if (this.intervalTimeValue >= this.IntervalTime) { this.intervalTimeValue = -1; this.CurrentIndex++; if (this.CurrentIndex > this.Items.Count - 1) { this.CurrentIndex -= this.Items.Count; } 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 = 25f * (float)textItem.prev_rectf.Width;//动画时间由文字长度决定,速度为每个像素使用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); } } /// <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 = (float)(textItem.prev_char_rectf[i].X - (textItem.prev_char_rectf[i].X - (this.ClientRectangle.X + i * textItem.TextCharAvgWidth)) * e.progressTime); textItem.current_char_rectf[i] = new RectangleF(x, textItem.prev_char_rectf[i].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 = (float)(textItem.prev_char_rectf[i].X - ((textItem.prev_char_rectf[i].X - (this.ClientRectangle.X + i * textItem.TextCharAvgWidth))) * e.progressTime); textItem.current_char_rectf[i] = new RectangleF(x, textItem.prev_char_rectf[i].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 = 0; } 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]; 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(); } } 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]; LinearGradientBrush text_lgb = new LinearGradientBrush(textItem.current_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.Text.Substring(k, 1), textItem.TextFont, text_lgb, textItem.current_char_rectf[k]); } text_lgb.Dispose(); } } } protected override void OnResize(EventArgs e) { base.OnResize(e); this.InitializeTextRectangles(); } #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; 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 + (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 + (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); } 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]; for (int i = 0; i < len; i++) { textItem.TextChar[i] = textItem.Text.Substring(i, 1); textItem.prev_char_rectf[i] = new RectangleF(rect.Right + i * rect.Width, (rect.Height - text_size.Height) / 2, avg_w, text_size.Height); textItem.current_char_rectf[i] = textItem.prev_char_rectf[i]; } } 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]; for (int i = 0; i < len; i++) { textItem.TextChar[i] = textItem.Text.Substring(i, 1); textItem.prev_char_rectf[i] = new RectangleF(rect.Right + i * avg_w * 3, (rect.Height - text_size.Height) / 2, avg_w, text_size.Height); textItem.current_char_rectf[i] = textItem.prev_char_rectf[i]; } } } #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 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 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 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; } } /// <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