前 注:
这是自己平时根据自己需要写的一些小代码,未必对各看官有用。另外,这是根据个人想法而写,未必严谨和符合设计原则,若有任何不妥之处,还请不吝赐教。
说 明:
本文描述一个可滚动显示文本信息的状态栏标签控件。起因是某天领导说某些信息应该更醒目,让用户更容易注意到。于是就花了半天的时间做了这个东西。
基本的思路是用定时器每隔一段时间重绘文本,使其显示在控件的不同位置。
已经实现了关于滚动速度、精细度的控制功能,已经实现由右至左滚动显示的滚动方式。
使用方式:将向状态栏添加标签,再到设计器代码中将标签类型改为ToolStripStatusRollingLabel。
设计要点:
1、由于ToolStripStatusLabel无法设置文本的显示位置,因此需要重载OnPaint来更改文本的绘制逻辑。为了避免完全重绘控件,重写的OnPaint函数先调用ToolStripStatusLabel.OnPaint,再使用GDI在控件上绘制文本。
为了提供与ToolStripStatusLabel类似的使用接口,保留Text作为新控件中设置需滚动显示的文本内容的属性。由于ToolStripStatusLabel.OnPaint同样会绘制文本,若不特殊处理,控件将会同时显示一个滚动的文本和一个静止的文本。
因此,需要重载Text属性,将需要显示的文本内容另设变量存储(用于在重写的OnPaint中使用),并且使Text属性在运行时为空。另一方面,为保证用户体验,又需要Text属性在设计器中的值与所设置的值一致。这就是重写的Text属性的get访问器需要判断DesignMode的原因。
2、滚动的速度与精细度通过两个属性组合来控制:一、RollSpeed,设置定时器每次动作时,文本显示位置与上一次显示位置之间的距离;二、RedrawTimeSpan,设置定时器触发的时间间隔。RedrawTimeSpan越小,RoolSpeed越大,滚动的速度越快;RedrawTimeSpan越小,RoolSpeed越小,滚动的精细度越高。
3、控件的滚动样式由文本在控件上的显示位置的变化规律决定。便于扩展滚动样式,计算文本显示位置的逻辑被抽取出来,形成一个单独的接口IControlDataRollOffsetGenerater。
4、IControlDataRollOffsetGenerater提供获取一个在某个区间内滚动的数值的接口。对我能想到的所有滚动样式,文本的每个显示位置均可由控件宽度、文本宽度及上次显示位置和每次的偏移量确定。在此接口中,这些数据分别由WindowWidth、DataWidth、Offset和Speed表示。
5、特殊的滚动效果:文本朝某个方向滚动,消失的部分文本从控件的另一端显示出来。可以这样实现:将用户要显示的文本用空白拼接起来显示,空白的长度等于控件宽度与文本长度的差。当然,这要求文本长度不能超过控件宽度。
结构图 :
源代码 :
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace CommonLibrary.ExtendedControl { /// <summary> /// 滚动显示的状态栏标签类 /// </summary> public class ToolStripStatusRollingLabel : ToolStripStatusLabel { public ToolStripStatusRollingLabel() { StringRect.X = this.Padding.Left; StringRect.Y = this.Padding.Top; StringRect.Height = Size.Height - Padding.Top - Padding.Bottom; Brush = new SolidBrush(this.ForeColor); ControlDataRollOffsetGenerater = ControlDataRollOffsetGeneraterFactory.Instance.GetControlDataRollOffsetGenerater(this.RollSytle,0,0,3,0); this.UpdateTimer = new Timer(); this.UpdateTimer.Interval = 500; this.UpdateTimer.Enabled = true; this.UpdateTimer.Tick += delegate { this.OnTick(); }; } #region 辅助 /// <summary> /// 定时器 /// </summary> private Timer UpdateTimer; /// <summary> /// 笔刷 /// </summary> SolidBrush Brush; /// <summary> /// 用于显示内容的矩形 /// </summary> Rectangle StringRect = Rectangle.Empty; /// <summary> /// 滚动的偏移量生成器 /// </summary> IControlDataRollOffsetGenerater ControlDataRollOffsetGenerater; /// <summary> /// 图形 /// </summary> Graphics Graphics; #endregion #region 数据 /// <summary> /// 显示内容 /// </summary> private string _Text; /// <summary> /// 滚动距离 /// </summary> private int _RollDistance = 0; /// <summary> /// 控件内容的滚动样式 /// </summary> private EnumTextRollStyle _RollStyle = EnumTextRollStyle.TurnLeft; /// <summary> /// 文本宽度 /// </summary> private int _TextWidth; #endregion #region 属性 /// <summary> /// 文本内容 /// </summary> public override string Text { get { if (DesignMode) { return _Text; } else { return string.Empty; } } set { if (_Text == value) return; _Text = value; if (!DesignMode) { if (Graphics != null) TextWidth = (int)(Graphics.MeasureString(value, this.Font).Width); this.Invalidate(); } } } /// <summary> /// 文本宽度 /// </summary> public int TextWidth { get { return _TextWidth; } set { if (_TextWidth == value) return; _TextWidth = value; if (ControlDataRollOffsetGenerater != null) ControlDataRollOffsetGenerater.DataWidth = value; } } /// <summary> /// 前景色 /// </summary> public override Color ForeColor { get { return base.ForeColor; } set { Brush = new SolidBrush(base.ForeColor); base.ForeColor = value; //this.Invalidate(); } } /// <summary> /// 控件大小 /// </summary> public override Size Size { get { return base.Size; } set { if (this.Size == value) return; base.Size = value; StringRect.Height = Size.Height - Padding.Top - Padding.Bottom; if (ControlDataRollOffsetGenerater != null) ControlDataRollOffsetGenerater.WindowWidth = this.Size.Width - Padding.Left - Padding.Right; } } /// <summary> /// 内部间距 /// </summary> public override Padding Padding { get { return base.Padding; } set { if (value == Padding) return; base.Padding = value; StringRect.Y = Padding.Top; StringRect.Height = Size.Height - Padding.Top - Padding.Bottom; if (ControlDataRollOffsetGenerater != null) ControlDataRollOffsetGenerater.WindowWidth = this.Size.Width - Padding.Left - Padding.Right; } } /// <summary> /// 滚动速度 /// </summary> public int RedrawTimeSpan { get { return UpdateTimer.Interval; } set { UpdateTimer.Interval = value; } } /// <summary> /// 滚动速度:每次刷新文本显示位置的间距 /// </summary> public int RoolSpeed { get { return ControlDataRollOffsetGenerater.Speed; } set { ControlDataRollOffsetGenerater.Speed = value; } } /// <summary> /// 当前显示位置与初始位置的距离 /// </summary> public int RollDistance { get { return _RollDistance; } set { _RollDistance = value; } } /// <summary> /// 控件内容的滚动样式 /// </summary> public EnumTextRollStyle RollSytle { get { return _RollStyle; } set { if (_RollStyle == value) return; _RollStyle = value; ControlDataRollOffsetGenerater = ControlDataRollOffsetGeneraterFactory.Instance.GetControlDataRollOffsetGenerater(value, StringRect.Width, TextWidth, this.RedrawTimeSpan, this.RollDistance); } } #endregion private void OnTick() { //if (ControlDataRollOffsetGenerater == null) return; RollDistance = ControlDataRollOffsetGenerater.GetNextOffset(); StringRect.X = this.Padding.Left + RollDistance; StringRect.Width = ControlDataRollOffsetGenerater.WindowWidth - RollDistance; this.Invalidate(); } protected override void OnPaint(PaintEventArgs e) { if (Graphics == null) { Graphics = e.Graphics; TextWidth = (int)(Graphics.MeasureString(_Text, this.Font).Width); } e.Graphics.DrawString(_Text, this.Font, Brush, StringRect); base.OnPaint(e); } } /// <summary> /// 控件文本内容滚动的样式 /// </summary> public enum EnumTextRollStyle { /// <summary> /// 向左滚动 /// </summary> TurnLeft, /// <summary> /// 向左滚动 /// </summary> TurnRight, /// <summary> /// 来回滚动 /// </summary> PingPong } public class ControlDataRollOffsetGeneraterFactory { #region 单例 static Singleton<ControlDataRollOffsetGeneraterFactory> Singleton; public static ControlDataRollOffsetGeneraterFactory Instance { get { if (Singleton == null) Singleton = new Singleton<ControlDataRollOffsetGeneraterFactory>(); return Singleton.Instance; } } #endregion /// <summary> /// 获取一个数据滚动的偏移量生成器 /// </summary> /// <param name="RollStyle"></param> /// <param name="WindowWidth"></param> /// <param name="DataWidth"></param> /// <param name="Speed"></param> /// <param name="InitOffset"></param> /// <returns></returns> public IControlDataRollOffsetGenerater GetControlDataRollOffsetGenerater(EnumTextRollStyle RollStyle, int WindowWidth, int DataWidth, int Speed, int InitOffset) { switch (RollStyle) { case EnumTextRollStyle.TurnLeft: return new ControlDataRollToLeftOffsetGenerater(WindowWidth, DataWidth, Speed, InitOffset); } return null; } } /// <summary> /// 控件内容滚动偏移量生成器的接口 /// </summary> public interface IControlDataRollOffsetGenerater { /// <summary> /// 显示区域宽度 /// </summary> int WindowWidth { get;set;} /// <summary> /// 数据宽度 /// </summary> int DataWidth { get;set;} /// <summary> /// 偏移量 /// </summary> int Offset { get;set;} /// <summary> /// 移动速度 /// </summary> int Speed { get;set;} /// <summary> /// 获取下一个偏移量 /// </summary> /// <returns>新偏移量</returns> int GetNextOffset(); } /// <summary> /// 控件内容滚动偏移量生成器基类 /// </summary> public class ControlDataRollOffsetGeneraterBase : IControlDataRollOffsetGenerater { public ControlDataRollOffsetGeneraterBase() { } public ControlDataRollOffsetGeneraterBase(int WindowWidth, int DataWidth, int Speed) { this._WindowWidth = WindowWidth; this._DataWidth = DataWidth; this._Speed = Speed; } public ControlDataRollOffsetGeneraterBase(int WindowWidth, int DataWidth, int Speed, int InitOffset) { this._WindowWidth = WindowWidth; this._DataWidth = DataWidth; this._Speed = Speed; this._Offset = InitOffset; } #region 数据 /// <summary> /// 显示区域宽度 /// </summary> int _WindowWidth; /// <summary> /// 数据宽度 /// </summary> int _DataWidth; /// <summary> /// 偏移量 /// </summary> int _Offset; /// <summary> /// 移动速度 /// </summary> int _Speed; #endregion #region 属性 /// <summary> /// 显示区域宽度 /// </summary> public virtual int WindowWidth { get { return _WindowWidth; } set { _WindowWidth = value; } } /// <summary> /// 数据宽度 /// </summary> public virtual int DataWidth { get { return _DataWidth; } set { _DataWidth = value; } } /// <summary> /// 偏移量 /// </summary> public virtual int Offset { get { return _Offset; } set { _Offset = value; } } /// <summary> /// 移动速度 /// </summary> public virtual int Speed { get { return _Speed; } set { _Speed = value; } } #endregion /// <summary> /// 获取下一个偏移量 /// </summary> /// <returns>新偏移量</returns> public virtual int GetNextOffset() { return 0; } } /// <summary> /// 控件内容向左滚动的偏移量生成器 /// </summary> public class ControlDataRollToLeftOffsetGenerater : ControlDataRollOffsetGeneraterBase { public ControlDataRollToLeftOffsetGenerater(int WindowWidth, int DataWidth, int Speed) : base(WindowWidth, DataWidth, Speed) { //RollWidth = WindowWidth + DataWidth; } public ControlDataRollToLeftOffsetGenerater(int WindowWidth, int DataWidth, int Speed, int InitOffset) : base(WindowWidth, DataWidth, Speed) { //RollWidth = WindowWidth + DataWidth; } #region 数据 //private int _RollWidth; #endregion #region 属性 public override int WindowWidth { get { return base.WindowWidth; } set { base.WindowWidth = value; //RollWidth = DataWidth + WindowWidth; } } public override int DataWidth { get { return base.DataWidth; } set { base.DataWidth = value; //RollWidth = DataWidth + WindowWidth; } } //public int RollWidth //{ // get // { // return _RollWidth; // } // set // { // if (value < 0) _RollWidth = 0; // } //} #endregion /// <summary> /// 获取一个新的偏移量 /// </summary> /// <returns></returns> public override int GetNextOffset() { if (Offset < -DataWidth) { Offset = WindowWidth; } else { Offset -= Speed; } return Offset; } } }