[Serializable, StructLayout(LayoutKind.Sequential), TypeConverter(typeof(CircleFConverter))]
public sealed class CircleF : ICloneable
{
public static CircleF Empty = new CircleF();
PointF _pivot;//圆心
float _radius;//半径
public CircleF() : this(default(PointF), 0)
{
}
public CircleF(RectangleF rectf)
{
_pivot = GetCirclePivot(rectf);
_radius = GetCircleRadius(rectf);
}
public CircleF(PointF pivot, float radius)
{
_pivot = pivot;
_radius = radius;
}
public static PointF GetCirclePivot(RectangleF rect)
{
return new PointF(rect.X + rect.Width / 2.0f, rect.Y + rect.Height / 2.0f);
}
public static float GetCircleRadius(RectangleF rect)
{
return Math.Min(rect.Width / 2.0f, rect.Height / 2.0f);
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public PointF Pivot { get { return _pivot; } set { _pivot = value; } }
public float Radius
{
get { return _radius; }
set
{
if (value >= 0)
_radius = value;
}
}
public RectangleF GetBounds()
{
return new RectangleF(_pivot.X - _radius, _pivot.Y - _radius, _radius * 2.0f, _radius * 2.0f);
}
public static RectangleF GetBounds(RectangleF rect)
{
PointF p = GetCirclePivot(rect);
float d = GetCircleRadius(rect);
return new RectangleF(p.X - d, p.Y - d, d * 2.0f, d * 2.0f);
}
public bool Contains(float x, float y)
{
return Contains(new PointF(x, y));
}
public bool Contains(PointF p)
{
return Distance(p) <= _radius;
}
public float Distance(CircleF c)
{
return Distance(c.Pivot);
}
public float Distance(PointF p)
{
return (float)Math.Sqrt((p.X - _pivot.X) * (p.X - _pivot.X) + (p.Y - _pivot.Y) * (p.Y - _pivot.Y));
}
public object Clone()
{
return this.MemberwiseClone();
}
public PointF PointOnPath(double sweepAngle)
{
return new PointF(_pivot.X + _radius * (float)Math.Cos(sweepAngle * (Math.PI / 180.0d)), _pivot.Y - _radius * (float)Math.Sin(sweepAngle * (Math.PI / 180.0d)));
}
public void Offset(float x, float y)
{
_pivot.X += x;
_pivot.Y += y;
}
public void Offset(System.Drawing.PointF pos)
{
Offset(pos.X, pos.Y);
}
public void Inflate(float d)
{
Radius += d;
}
public static bool operator !=(CircleF left, CircleF right)
{
return left._pivot == right._pivot && left._radius == right._radius;
}
public static bool operator ==(CircleF left, CircleF right)
{
return !(left != right);
}
public override string ToString()
{
return string.Format("{{Pivot={0},Radius={1}}}", _pivot, _radius);
}
public static implicit operator RectangleF(CircleF c)
{
return c.GetBounds();
}
}
public class CircleFConverter : TypeConverter
{
public CircleFConverter()
{
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
return true;
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (value is CircleF)
{
if (destinationType == typeof(string))
{
CircleF c = value as CircleF;
return string.Format("({0},{1}),{2}", c.Pivot.X, c.Pivot.Y, c.Radius);
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
return true;
else
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value is string)
{
string[] ti = ((string)value).Split(',');
float x = float.Parse(ti[0].Substring(1));
float y = float.Parse(ti[1].Substring(0, ti[1].Length - 1));
float r = float.Parse(ti[2]);
return new CircleF(new PointF(x, y), r);
}
return base.ConvertFrom(context, culture, value);
}
public override PropertyDescriptorCollection? GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes)
{
return TypeDescriptor.GetProperties(typeof(CircleF), attributes).Sort(new string[] { "Radius", "Pivot" });
}
public override bool GetPropertiesSupported(ITypeDescriptorContext? context)
{
return true;
}
}
/// <summary>
/// 封装生成高级衍生图形的各种静态方法/// </summary>
public static class ExtendedShapes
{
public static PointF[] CreateRegularPolygon(PointF pivot, float outterRadius, int points, float angleOffset)
{
if (outterRadius < 0) throw new ArgumentOutOfRangeException("outterRadius", "多边形的外接圆半径必须大于0");
if (points < 3) throw new ArgumentOutOfRangeException("points", "多边形的边数必须大于等于3");
PointF[] ret = new PointF[points];
CircleF c = new CircleF(pivot, outterRadius); float ang = 360.0f / points;
for (int i = 0; i < points; i++)
{
ret[i] = c.PointOnPath(angleOffset + i * ang);
}
return ret;
}
public static PointF[] CreateRegularPolygon(PointF pivot, float outterRadius, int points)
{
return CreateRegularPolygon(pivot, outterRadius, points, 0.0f);
}
public static GraphicsPath CreateStar(PointF pivot, float outterRadius, float innerRadius, int points, float angleOffset)
{
if (outterRadius <= innerRadius) throw new ArgumentException("参数 outterRadius必须大于innerRadius。");
if (points < 2) throw new ArgumentOutOfRangeException("points"); GraphicsPath gp = new GraphicsPath();
CircleF outter = new CircleF(pivot, outterRadius);
CircleF inner = new CircleF(pivot, innerRadius); float ang = 360.0f / points;
for (int i = 0; i < points; i++)
{
gp.AddLine(outter.PointOnPath(angleOffset + i * ang), inner.PointOnPath(angleOffset + i * ang + ang / 2.0f));
}
gp.CloseFigure();
return gp;
}
public static GraphicsPath CreateStar(PointF pivot, float outterRadius, float innerRadius, int points)
{
return CreateStar(pivot, outterRadius, innerRadius, points, 0);
}
public static GraphicsPath CreateChamferBox(RectangleF rect, float radius)
{
GraphicsPath gp = new GraphicsPath();
if (radius <= 0) { gp.AddRectangle(rect); return gp; }
gp.AddArc(rect.Right - 2 * radius, rect.Top, radius * 2, radius * 2, 270, 90);
gp.AddArc(rect.Right - radius * 2, rect.Bottom - radius * 2, radius * 2, radius * 2, 0, 90);
gp.AddArc(rect.Left, rect.Bottom - 2 * radius, 2 * radius, 2 * radius, 90, 90);
gp.AddArc(rect.Left, rect.Top, 2 * radius, 2 * radius, 180, 90); gp.CloseFigure();
return gp;
}
public static List<PointF[]> CreateWaitCircleSpokes(PointF pivot, float outterRadius, float innerRadius, int spokes, float angleOffset)
{
if (spokes < 1) throw new ArgumentException("参数spokes必须大于等于。"); List<PointF[]> lst = new List<PointF[]>();
CircleF outter = new CircleF(pivot, outterRadius); CircleF inner = new CircleF(pivot, innerRadius);
float ang = 360.0f / spokes;
for (int i = 0; i < spokes; i++)
lst.Add(new PointF[2] { outter.PointOnPath(angleOffset + i * ang), inner.PointOnPath(angleOffset + i * ang) });
return lst;
}
public static List<PointF[]> CreateWaitCircleSpokes(PointF pivot, float outterRadius, float innerRadius, int spokes)
{
return CreateWaitCircleSpokes(pivot, outterRadius, innerRadius, spokes,
0.0f);
}
}
public class WaitingControl : Control
{
private int _numberofspokes = 20; //控件包含的总的辐条数量
private int _hotspokes = 5; //活动辐条数
private float _innerRadius; //内环半径,外环半径根据控件的大小计算
private Color _spokeColor = Color.LightGray;//辐条颜色
private Color _hotSpokeColor = Color.Gray; //活动辐条颜色
private int _thickness = 3; //辐条的宽度
private bool _antialias = true; //控件绘制时是否反走样
private bool _colockWise = true; //活动辐条是否顺时针旋转
private List<PointF[]> _spokes; //辐条轮廓线列表
protected Color[] _palette; //渐变色调色板
private System.Windows.Forms.Timer _timer; //计时器
private Pen _pen; //绘制WaitingCircle的画笔
private int _offset = 0; //活动辐条偏移量
public WaitingControl()
{
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
_timer = new System.Windows.Forms.Timer();
_timer.Tick += _timer_Tick; ;
_pen = new Pen(_spokeColor, _thickness);
this.Text = "请稍等";
this.Size = new Size(120, 120);
GeneratePalette();
}
private void _timer_Tick(object? sender, EventArgs e)
{
Invalidate();
_offset = ((_colockWise ? --_offset : ++_offset) + _numberofspokes) % _numberofspokes;
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
CircleF c = new CircleF(ClientRectangle); c.Inflate(-5.0f);
_innerRadius = c.Radius / 2.0f;
_spokes = ExtendedShapes.CreateWaitCircleSpokes(c.Pivot, c.Radius, _innerRadius, _numberofspokes);
}
public float InnerRadius
{
get { return _innerRadius; }
set
{
if (_innerRadius != value && value > 0)
_innerRadius = value;
CircleF c = new CircleF(ClientRectangle);
c.Inflate(-5.0f);
_spokes = ExtendedShapes.CreateWaitCircleSpokes(c.Pivot, c.Radius, _innerRadius, _numberofspokes);
Invalidate();
}
}
public int NumberOfSpokes
{
get { return _numberofspokes; }
set
{
_numberofspokes = Math.Max(2, Math.Max(_hotspokes, value)); CircleF c = new CircleF(ClientRectangle);
c.Inflate(-5.0f);
_spokes = ExtendedShapes.CreateWaitCircleSpokes(c.Pivot, c.Radius, _innerRadius, _numberofspokes);
Invalidate();
}
}
public LineCap StartCap
{
get { return _pen.StartCap; }
set
{
_pen.StartCap = value; Invalidate();
}
}
public LineCap EndCap
{
get { return _pen.EndCap; }
set { _pen.EndCap = value; Invalidate(); }
}
public int Thickness
{
get { return _thickness; }
set
{
if (value > 0)
_thickness = value; _pen.Width = _thickness; Invalidate();
}
}
protected virtual void GeneratePalette()
{
_palette = new Color[_hotspokes];
float a = (float)(_hotSpokeColor.A - _spokeColor.A) / (float)_hotspokes;
float r = (float)(_hotSpokeColor.R - _spokeColor.R) / (float)_hotspokes;
float g = (float)(_hotSpokeColor.G - _spokeColor.G) / (float)_hotspokes;
float b = (float)(_hotSpokeColor.B - _spokeColor.B) / (float)_hotspokes;
for (int i = 0; i < _hotspokes; i++)
{
_palette[i] = Color.FromArgb(_hotSpokeColor.A - (int)(i * a),
_hotSpokeColor.R - (int)(i * r), _hotSpokeColor.G - (int)(i * g), _hotSpokeColor.B - (int)(i * b));
}
}
public int Speed
{
get { return _timer.Interval; }
set { _timer.Interval = value; }
}
public bool Activate
{
get { return _timer.Enabled; }
set { _timer.Enabled = value; this.Invalidate(); }
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
DrawSpokes(e.Graphics);
}
protected void DrawSpokes(Graphics g)
{
g.SmoothingMode = _antialias ? SmoothingMode.AntiAlias : SmoothingMode.Default;
if (!Activate)
{
_pen.Color = _spokeColor;
foreach (PointF[] spoke in _spokes)
{
g.DrawLines(_pen, spoke);
}
}
else
{
List<int> hot = new List<int>(); //存储活动辐条的索引
for (int i = 0; i < _hotspokes; i++) //计算活动辐条的索引
{
int index = ((_colockWise ? _offset - i : _offset + i) + _numberofspokes) % _numberofspokes;
hot.Add(index);
}
_pen.Color = _spokeColor;
for (int i = 0; i < _numberofspokes; i++)//首先绘制非活动辐条
{
if (!hot.Contains(i))
{
g.DrawLines(_pen, _spokes[i]);
}
}
for (int i = 0; i < _hotspokes; i++) //绘制活动辐条
{
_pen.Color = _palette[_hotspokes - 1 - i];
g.DrawLines(_pen, _spokes[hot[i]]);
}
}
if (!string.IsNullOrWhiteSpace(this.Text))
{
SizeF sf = this.Text.MeasureString(this.Font);
using SolidBrush solid=new SolidBrush(this.ForeColor);
g.DrawString(this.Text, this.Font, solid, new PointF((this.Width-sf.Width)/2,(this.Height-sf.Height)/2));
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_pen.Dispose();
_timer?.Dispose();
}
}
}