之前开发一个小工具,内部实现一个状态机,并显示状态机当前状态及状态间的转移过程。我使用了Stateless开源类库及一个开源自定义箭头控件。自定义箭头控件是HZHControls其中一个控件,我单独把它从源码中独立出来。主要对代码做了以下改动:
以上涉及到的相关项目的链接如下
自定义箭头控件的代码很有学习价值,可以在其基础上扩展出其它形状的控件。使用时新建一个用户控件然后复制代码即可,代码如下:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Windows.Forms;using System.Drawing;using System.Drawing.Drawing2D;using System.ComponentModel;namespace ExtControls{ public partial class Arrow : UserControl { private Color arrowColor = Color.DarkGray; /// <summary> /// 获取设置箭头颜色 /// </summary> [Description("箭头颜色"), Category("自定义")] public Color ArrowColor { get { return arrowColor; } set { arrowColor = value; Refresh(); } } private Color? borderColor = null; /// <summary> /// 获取或设置箭头边框颜色 /// </summary> [Description("箭头边框颜色,为空则无边框"), Category("自定义")] public Color? BorderColor { get { return borderColor; } set { borderColor = value; Refresh(); } } /// <summary> /// 箭头方向 /// </summary> private ArrowDirection direction = ArrowDirection.Right; /// <summary> /// 获取或设置箭头方向 /// </summary> /// <value>The direction.</value> [Description("获取或设置箭头方向"), Category("自定义")] public ArrowDirection Direction { get { return direction; } set { direction = value; ResetMyPath(); Refresh(); } } private string text; /// <summary> /// 与控件关联的文本。 /// </summary> [Bindable(true)] [Browsable(true)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] [EditorBrowsable(EditorBrowsableState.Always)] [Localizable(true)] [Description("与控件关联的文本"), Category("自定义")] public override string Text { get { return text; } set { text = value; Refresh(); } } /// <summary> /// 图形空间 /// </summary> GraphicsPath myPath; /// <summary> /// 初始化UCArrow类的新实例。 /// </summary> public Arrow() { this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); this.SetStyle(ControlStyles.DoubleBuffer, true); this.SetStyle(ControlStyles.ResizeRedraw, true); this.SetStyle(ControlStyles.Selectable, true); this.SetStyle(ControlStyles.SupportsTransparentBackColor, true); this.SetStyle(ControlStyles.UserPaint, true); this.ForeColor = Color.White; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.SizeChanged += UCArrow_NeedResetChanged; this.Size = new Size(100, 50); this.ForeColorChanged += UCArrow_NeedResetChanged; this.ForeColor = Color.Black; this.FontChanged += UCArrow_NeedResetChanged; } /// <summary> /// 需要重绘控件的事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void UCArrow_NeedResetChanged(object sender, EventArgs e) { ResetMyPath(); } /// <summary> /// 重置图形 /// </summary> private void ResetMyPath() { Point[] ps = null; switch (direction) { case ArrowDirection.Left: ps = new Point[] { new Point(0,this.Height/2), new Point(40,0), new Point(40,this.Height/4), new Point(this.Width-1,this.Height/4), new Point(this.Width-1,this.Height-this.Height/4), new Point(40,this.Height-this.Height/4), new Point(40,this.Height), new Point(0,this.Height/2) }; break; case ArrowDirection.Right: ps = new Point[] { new Point(0,this.Height/4), new Point(this.Width-40,this.Height/4), new Point(this.Width-40,0), new Point(this.Width-1,this.Height/2), new Point(this.Width-40,this.Height), new Point(this.Width-40,this.Height-this.Height/4), new Point(0,this.Height-this.Height/4), new Point(0,this.Height/4) }; break; case ArrowDirection.Top: ps = new Point[] { new Point(this.Width/2,0), new Point(this.Width,40), new Point(this.Width-this.Width/4,40), new Point(this.Width-this.Width/4,this.Height-1), new Point(this.Width/4,this.Height-1), new Point(this.Width/4,40), new Point(0,40), new Point(this.Width/2,0), }; break; case ArrowDirection.Bottom: ps = new Point[] { new Point(this.Width-this.Width/4,0), new Point(this.Width-this.Width/4,this.Height-40), new Point(this.Width,this.Height-40), new Point(this.Width/2,this.Height-1), new Point(0,this.Height-40), new Point(this.Width/4,this.Height-40), new Point(this.Width/4,0), new Point(this.Width-this.Width/4,0), }; break; case ArrowDirection.Left_Right: ps = new Point[] { new Point(0,this.Height/2), new Point(40,0), new Point(40,this.Height/4), new Point(this.Width-40,this.Height/4), new Point(this.Width-40,0), new Point(this.Width-1,this.Height/2), new Point(this.Width-40,this.Height), new Point(this.Width-40,this.Height-this.Height/4), new Point(40,this.Height-this.Height/4), new Point(40,this.Height), new Point(0,this.Height/2), }; break; case ArrowDirection.Top_Bottom: ps = new Point[] { new Point(this.Width/2,0), new Point(this.Width,40), new Point(this.Width-this.Width/4,40), new Point(this.Width-this.Width/4,this.Height-40), new Point(this.Width,this.Height-40), new Point(this.Width/2,this.Height-1), new Point(0,this.Height-40), new Point(this.Width/4,this.Height-40), new Point(this.Width/4,40), new Point(0,40), new Point(this.Width/2,0), }; break; } myPath = new GraphicsPath(); myPath.AddLines(ps); myPath.CloseAllFigures(); } /// <summary> /// 引发Control.Paint 事件。 /// </summary> /// <param name="e"></param> protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); var g = e.Graphics; // 设置GDI高质量模式抗锯齿 g.SmoothingMode = SmoothingMode.AntiAlias; g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.CompositingQuality = CompositingQuality.HighQuality; g.FillPath(new SolidBrush(arrowColor), myPath); if (borderColor != null && borderColor != Color.Empty) g.DrawPath(new Pen(new SolidBrush(borderColor.Value)), myPath); if (!string.IsNullOrEmpty(text)) { var size = g.MeasureString(Text, Font); g.DrawString(Text, Font, new SolidBrush(ForeColor), new PointF((this.Width - size.Width) / 2, (this.Height - size.Height) / 2)); } } } /// <summary> /// 箭头方向的描述枚举 /// </summary> public enum ArrowDirection { /// <summary> /// 向左 /// </summary> Left, /// <summary> /// 向右 /// </summary> Right, /// <summary> /// 向上 /// </summary> Top, /// <summary> /// 向下 /// </summary> Bottom, /// <summary> /// 向左向右 /// </summary> Left_Right, /// <summary> /// 向上向下 /// </summary> Top_Bottom }}