前 注:
这是自己平时根据自己需要写的一些小代码,未必对各看官有用。另外,这是根据个人想法而写,未必严谨和符合设计原则,若有任何不妥之处,还请不吝赐教。
说 明:
本文描述一个可滚动显示文本信息的状态栏标签控件。起因是某天领导说某些信息应该更醒目,让用户更容易注意到。于是就花了半天的时间做了这个东西。
基本的思路是用定时器每隔一段时间重绘文本,使其显示在控件的不同位置。
已经实现了关于滚动速度、精细度的控制功能,已经实现由右至左滚动显示的滚动方式。
使用方式:将向状态栏添加标签,再到设计器代码中将标签类型改为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;
}
}
}