条件
入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。
GitHub:https://github.com/kwwwvagaa/NetWinformControl
码云:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
如果觉得写的还行,请点个 star 支持一下吧
接待前来互换探究: 企鹅群568015492
麻烦博客下方点个【保举】,谢谢
NuGet
- Install-Package HZH_Controls
复制代码
目次
https://www.cnblogs.com/bfyx/p/11364884.html
用处及效果
预备工作
GDI+画的,不会的可以先百度了解下
开始
添加一个类UCRadarChart ,继承 UserControl
添加一些控制属性
属性改变时处置惩罚工作区域
- 1 /// <summary>
- 2 /// Handles the SizeChanged event of the UCRadarChart control.
- 3 /// </summary>
- 4 /// <param name="sender">The source of the event.</param>
- 5 /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
- 6 void UCRadarChart_SizeChanged(object sender, EventArgs e)
- 7 {
- 8 ResetWorkingRect();
- 9 }
- 10
- 11 /// <summary>
- 12 /// Resets the working rect.
- 13 /// </summary>
- 14 private void ResetWorkingRect()
- 15 {
- 16 if (lines != null && lines.Length > 0)
- 17 {
- 18 using (Graphics g = this.CreateGraphics())
- 19 {
- 20 foreach (var item in lines)
- 21 {
- 22 var s = g.MeasureString(item.Name, Font);
- 23 if (s.Width > lineValueTypeSize.Width)
- 24 lineValueTypeSize = s;
- 25 }
- 26 }
- 27 }
- 28 var lineTypePanelHeight = 0f;
- 29 if (lineValueTypeSize != SizeF.Empty)
- 30 {
- 31 intLineValueComCount = (int)(this.Width / (lineValueTypeSize.Width + 25));
- 32
- 33 intLineValueRowCount = lines.Length / intLineValueComCount;
- 34 if (lines.Length % intLineValueComCount != 0)
- 35 {
- 36 intLineValueRowCount++;
- 37 }
- 38 lineTypePanelHeight = (lineValueTypeSize.Height + 10) * intLineValueRowCount;
- 39 }
- 40 var min = Math.Min(this.Width, this.Height - titleSize.Height - lineTypePanelHeight);
- 41 var rectWorking = new RectangleF((this.Width - min) / 2 + 10, titleSize.Height + lineTypePanelHeight + 10, min - 10, min - 10);
- 42 //处置惩罚文字
- 43 float fltSplitAngle = 360F / radarPositions.Length;
- 44 float fltRadiusWidth = rectWorking.Width / 2;
- 45 float minX = rectWorking.Left;
- 46 float maxX = rectWorking.Right;
- 47 float minY = rectWorking.Top;
- 48 float maxY = rectWorking.Bottom;
- 49 using (Graphics g = this.CreateGraphics())
- 50 {
- 51 PointF centrePoint = new PointF(rectWorking.Left + rectWorking.Width / 2, rectWorking.Top + rectWorking.Height / 2);
- 52 for (int i = 0; i < radarPositions.Length; i++)
- 53 {
- 54 float fltAngle = 270 + fltSplitAngle * i;
- 55 fltAngle = fltAngle % 360;
- 56 PointF _point = GetPointByAngle(centrePoint, fltAngle, fltRadiusWidth);
- 57 var _txtSize = g.MeasureString(radarPositions[i].Text, Font);
- 58 if (_point.X < centrePoint.X)//左
- 59 {
- 60 if (_point.X - _txtSize.Width < minX)
- 61 {
- 62 minX = rectWorking.Left + _txtSize.Width;
- 63 }
- 64 }
- 65 else//右
- 66 {
- 67 if (_point.X + _txtSize.Width > maxX)
- 68 {
- 69 maxX = rectWorking.Right - _txtSize.Width;
- 70 }
- 71 }
- 72 if (_point.Y < centrePoint.Y)//上
- 73 {
- 74 if (_point.Y - _txtSize.Height < minY)
- 75 {
- 76 minY = rectWorking.Top + _txtSize.Height;
- 77 }
- 78 }
- 79 else//下
- 80 {
- 81 if (_point.Y + _txtSize.Height > maxY)
- 82 {
- 83 maxY = rectWorking.Bottom - _txtSize.Height;
- 84 }
- 85 }
- 86 }
- 87 }
- 88
- 89 min = Math.Min(maxX - minX, maxY - minY);
- 90 m_rectWorking = new RectangleF(minX, minY, min, min);
- 91 }
复制代码
重绘
- 1 protected override void OnPaint(PaintEventArgs e)
- 2 {
- 3 base.OnPaint(e);
- 4 var g = e.Graphics;
- 5 g.SetGDIHigh();
- 6
- 7 if (!string.IsNullOrEmpty(title))
- 8 {
- 9 g.DrawString(title, titleFont, new SolidBrush(titleColor), new RectangleF(m_rectWorking.Left + (m_rectWorking.Width - titleSize.Width) / 2, m_rectWorking.Top - titleSize.Height - 10 - (intLineValueRowCount * (10 + lineValueTypeSize.Height)), titleSize.Width, titleSize.Height));
- 10 }
- 11
- 12 if (radarPositions.Length <= 2)
- 13 {
- 14 g.DrawString("至少必要3个极点", Font, new SolidBrush(Color.Black), m_rectWorking, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
- 15 return;
- 16 }
- 17
- 18 var y = m_rectWorking.Top - 20 - (intLineValueRowCount * (10 + lineValueTypeSize.Height));
- 19
- 20 for (int i = 0; i < intLineValueRowCount; i++)
- 21 {
- 22 var x = 0f;
- 23 int intCount = intLineValueComCount;
- 24 if (i == intLineValueRowCount - 1)
- 25 {
- 26 intCount = lines.Length % intLineValueComCount;
- 27
- 28 }
- 29 x = m_rectWorking.Left + (m_rectWorking.Width - intCount * (lineValueTypeSize.Width + 25)) / 2;
- 30
- 31 for (int j = 0; j < intCount; j++)
- 32 {
- 33 g.FillRectangle(new SolidBrush(lines[i * intLineValueComCount + j].LineColor.Value), new RectangleF(x + (lineValueTypeSize.Width + 25)*j, y + lineValueTypeSize.Height * i, 15, lineValueTypeSize.Height));
- 34 g.DrawString(lines[i * intLineValueComCount + j].Name, Font, new SolidBrush(lines[i * intLineValueComCount + j].LineColor.Value), new PointF(x + (lineValueTypeSize.Width + 25) * j + 20, y + lineValueTypeSize.Height * i));
- 35 }
- 36 }
- 37
- 38 float fltSplitAngle = 360F / radarPositions.Length;
- 39 float fltRadiusWidth = m_rectWorking.Width / 2;
- 40 float fltSplitRadiusWidth = fltRadiusWidth / splitCount;
- 41 PointF centrePoint = new PointF(m_rectWorking.Left + m_rectWorking.Width / 2, m_rectWorking.Top + m_rectWorking.Height / 2);
- 42
- 43 List<List<PointF>> lstRingPoints = new List<List<PointF>>(splitCount);
- 44 //分割点
- 45 for (int i = 0; i < radarPositions.Length; i++)
- 46 {
- 47 float fltAngle = 270 + fltSplitAngle * i;
- 48 fltAngle = fltAngle % 360;
- 49 for (int j = 0; j < splitCount; j++)
- 50 {
- 51 if (i == 0)
- 52 {
- 53 lstRingPoints.Add(new List<PointF>());
- 54 }
- 55 PointF _point = GetPointByAngle(centrePoint, fltAngle, fltSplitRadiusWidth * (splitCount - j));
- 56 lstRingPoints[j].Add(_point);
- 57 }
- 58 }
- 59
- 60 for (int i = 0; i < lstRingPoints.Count; i++)
- 61 {
- 62 var ring = lstRingPoints[i];
- 63 GraphicsPath path = new GraphicsPath();
- 64 path.AddLines(ring.ToArray());
- 65 if ((lstRingPoints.Count - i) % 2 == 0)
- 66 {
- 67 g.FillPath(new SolidBrush(splitEvenColor), path);
- 68 }
- 69 else
- 70 {
- 71 g.FillPath(new SolidBrush(splitOddColor), path);
- 72 }
- 73 }
- 74
- 75 //画环
- 76 foreach (var ring in lstRingPoints)
- 77 {
- 78 ring.Add(ring[0]);
- 79 g.DrawLines(new Pen(new SolidBrush(lineColor)), ring.ToArray());
- 80 }
- 81 //分割线
- 82 foreach (var item in lstRingPoints[0])
- 83 {
- 84 g.DrawLine(new Pen(new SolidBrush(lineColor)), centrePoint, item);
- 85 }
- 86
- 87 //值
- 88 for (int i = 0; i < lines.Length; i++)
- 89 {
- 90 var line = lines[i];
- 91 if (line.Values.Length != radarPositions.Length)//如果数据长度和节点长度不一致则不绘制
- 92 continue;
- 93 if (line.LineColor == null || line.LineColor == Color.Empty || line.LineColor == Color.Transparent)
- 94 line.LineColor = ControlHelper.Colors[i + 13];
- 95 List<PointF> ps = new List<PointF>();
- 96 for (int j = 0; j < radarPositions.Length; j++)
- 97 {
- 98 float fltAngle = 270 + fltSplitAngle * j;
- 99 fltAngle = fltAngle % 360;
- 100 PointF _point = GetPointByAngle(centrePoint, fltAngle, fltRadiusWidth * (float)(line.Values[j] / radarPositions[i].MaxValue));
- 101 ps.Add(_point);
- 102 }
- 103 ps.Add(ps[0]);
- 104 if (line.FillColor != null && line.FillColor != Color.Empty && line.FillColor != Color.Transparent)
- 105 {
- 106 GraphicsPath path = new GraphicsPath();
- 107 path.AddLines(ps.ToArray());
- 108 g.FillPath(new SolidBrush(line.FillColor.Value), path);
- 109 }
- 110 g.DrawLines(new Pen(new SolidBrush(line.LineColor.Value), 2), ps.ToArray());
- 111
- 112 for (int j = 0; j < radarPositions.Length; j++)
- 113 {
- 114 var item = ps[j];
- 115 g.FillEllipse(new SolidBrush(Color.White), new RectangleF(item.X - 3, item.Y - 3, 6, 6));
- 116 g.DrawEllipse(new Pen(new SolidBrush(line.LineColor.Value)), new RectangleF(item.X - 3, item.Y - 3, 6, 6));
- 117 if (line.ShowValueText)
- 118 {
- 119 var valueSize = g.MeasureString(line.Values[j].ToString("0.##"), Font);
- 120 g.DrawString(line.Values[j].ToString("0.##"), Font, new SolidBrush(line.LineColor.Value), new PointF(item.X - valueSize.Width / 2, item.Y - valueSize.Height - 5));
- 121 }
- 122 }
- 123 }
- 124
- 125 //文本
- 126
- 127 for (int i = 0; i < radarPositions.Length; i++)
- 128 {
- 129 PointF point = lstRingPoints[0][i];
- 130 var txtSize = g.MeasureString(radarPositions[i].Text, Font);
- 131
- 132 if (point.X == centrePoint.X)
- 133 {
- 134 if (point.Y > centrePoint.Y)
- 135 {
- 136 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - txtSize.Width / 2, point.Y + 10));
- 137 }
- 138 else
- 139 {
- 140 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - txtSize.Width / 2, point.Y - 10 - txtSize.Height));
- 141 }
- 142 }
- 143 else if (point.Y == centrePoint.Y)
- 144 {
- 145 if (point.X < centrePoint.X)
- 146 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - 10 - txtSize.Width, point.Y - txtSize.Height / 2));
- 147 else
- 148 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + 10, point.Y - txtSize.Height / 2));
- 149 }
- 150 else if (point.X < centrePoint.X)//左
- 151 {
- 152 if (point.Y < centrePoint.Y)//左上
- 153 {
- 154 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - 10 - txtSize.Width, point.Y - 10 + txtSize.Height / 2));
- 155 }
- 156 else//左下
- 157 {
- 158 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - 10 - txtSize.Width, point.Y + 10 - txtSize.Height / 2));
- 159 }
- 160 }
- 161 else
- 162 {
- 163 if (point.Y < centrePoint.Y)//右上
- 164 {
- 165 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + 10, point.Y - 10 + txtSize.Height / 2));
- 166 }
- 167 else//右下
- 168 {
- 169 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + 10, point.Y + 10 - txtSize.Height / 2));
- 170 }
- 171 }
- 172 }
- 173
- 174 }
复制代码
辅助函数
- 1 #region 根据中心点、角度、半径盘算圆边坐标点 English:Calculating the coordinate points of circular edge according to the center point, angle and radius
- 2 /// <summary>
- 3 /// 功能形貌:根据中心点、角度、半径盘算圆边坐标点 English:Calculating the coordinate points of circular edge according to the center point, angle and radius
- 4 /// 作 者:HZH
- 5 /// 创建日期:2019-09-25 09:46:32
- 6 /// 任务编号:POS
- 7 /// </summary>
- 8 /// <param name="centrePoint">centrePoint</param>
- 9 /// <param name="fltAngle">fltAngle</param>
- 10 /// <param name="fltRadiusWidth">fltRadiusWidth</param>
- 11 /// <returns>返回值</returns>
- 12 private PointF GetPointByAngle(PointF centrePoint, float fltAngle, float fltRadiusWidth)
- 13 {
- 14 PointF p = centrePoint;
- 15 if (fltAngle == 0)
- 16 {
- 17 p.X += fltRadiusWidth;
- 18 }
- 19 else if (fltAngle == 90)
- 20 {
- 21 p.Y += fltRadiusWidth;
- 22 }
- 23 else if (fltAngle == 180)
- 24 {
- 25 p.X -= fltRadiusWidth;
- 26 }
- 27 else if (fltAngle == 270)
- 28 {
- 29 p.Y -= fltRadiusWidth;
- 30 }
- 31 else if (fltAngle > 0 && fltAngle < 90)
- 32 {
- 33 p.Y += (float)Math.Sin(Math.PI * (fltAngle / 180.00F)) * fltRadiusWidth;
- 34 p.X += (float)Math.Cos(Math.PI * (fltAngle / 180.00F)) * fltRadiusWidth;
- 35 }
- 36 else if (fltAngle > 90 && fltAngle < 180)
- 37 {
- 38 p.Y += (float)Math.Sin(Math.PI * ((180 - fltAngle) / 180.00F)) * fltRadiusWidth;
- 39 p.X -= (float)Math.Cos(Math.PI * ((180 - fltAngle) / 180.00F)) * fltRadiusWidth;
- 40 }
- 41 else if (fltAngle > 180 && fltAngle < 270)
- 42 {
- 43 p.Y -= (float)Math.Sin(Math.PI * ((fltAngle - 180) / 180.00F)) * fltRadiusWidth;
- 44 p.X -= (float)Math.Cos(Math.PI * ((fltAngle - 180) / 180.00F)) * fltRadiusWidth;
- 45 }
- 46 else if (fltAngle > 270 && fltAngle < 360)
- 47 {
- 48 p.Y -= (float)Math.Sin(Math.PI * ((360 - fltAngle) / 180.00F)) * fltRadiusWidth;
- 49 p.X += (float)Math.Cos(Math.PI * ((360 - fltAngle) / 180.00F)) * fltRadiusWidth;
- 50 }
- 51 return p;
- 52 }
- 53 #endregion
- 54
- 55 /// <summary>
- 56 /// Resets the size of the title.
- 57 /// </summary>
- 58 private void ResetTitleSize()
- 59 {
- 60 if (!string.IsNullOrEmpty(title))
- 61 {
- 62 using (Graphics g = this.CreateGraphics())
- 63 {
- 64 titleSize = g.MeasureString(title, titleFont);
- 65 }
- 66 }
- 67 else
- 68 {
- 69 titleSize = SizeF.Empty;
- 70 }
- 71 titleSize.Height += 20;
- 72 ResetWorkingRect();
- 73 }
复制代码
完整代码
- 1 // ***********************************************************************
- 2 // Assembly : HZH_Controls
- 3 // Created : 2019-09-25
- 4 //
- 5 // ***********************************************************************
- 6 // <copyright file="UCRadarChart.cs">
- 7 // Copyright by Huang Zhenghui(黄正辉) All, QQ group:568015492 QQ:623128629 Email:623128629@qq.com
- 8 // </copyright>
- 9 //
- 10 // Blog: https://www.cnblogs.com/bfyx
- 11 // GitHub:https://github.com/kwwwvagaa/NetWinformControl
- 12 // gitee:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
- 13 //
- 14 // If you use this code, please keep this note.
- 15 // ***********************************************************************
- 16 using System;
- 17 using System.Collections.Generic;
- 18 using System.Linq;
- 19 using System.Text;
- 20 using System.Windows.Forms;
- 21 using System.Drawing;
- 22 using System.Drawing.Drawing2D;
- 23 using System.ComponentModel;
- 24
- 25 namespace HZH_Controls.Controls
- 26 {
- 27 /// <summary>
- 28 /// Class UCRadarChart.
- 29 /// Implements the <see cref="System.Windows.Forms.UserControl" />
- 30 /// </summary>
- 31 /// <seealso cref="System.Windows.Forms.UserControl" />
- 32 public class UCRadarChart : UserControl
- 33 {
- 34 /// <summary>
- 35 /// The split count
- 36 /// </summary>
- 37 private int splitCount = 5;
- 38 /// <summary>
- 39 /// Gets or sets the split count.
- 40 /// </summary>
- 41 /// <value>The split count.</value>
- 42 [Browsable(true)]
- 43 [Category("自定义")]
- 44 [Description("获取或设置分隔份数")]
- 45 public int SplitCount
- 46 {
- 47 get { return splitCount; }
- 48 set
- 49 {
- 50 splitCount = value;
- 51 Invalidate();
- 52 }
- 53 }
- 54
- 55 /// <summary>
- 56 /// The split odd color
- 57 /// </summary>
- 58 private Color splitOddColor = Color.White;
- 59 /// <summary>
- 60 /// 分隔奇数栏配景色
- 61 /// </summary>
- 62 /// <value>The color of the split odd.</value>
- 63 [Browsable(true)]
- 64 [Category("自定义")]
- 65 [Description("获取或设置分隔奇数栏配景色")]
- 66 public Color SplitOddColor
- 67 {
- 68 get { return splitOddColor; }
- 69 set
- 70 {
- 71 splitOddColor = value;
- 72 Invalidate();
- 73 }
- 74 }
- 75 /// <summary>
- 76 /// The split even color
- 77 /// </summary>
- 78 private Color splitEvenColor = Color.FromArgb(232, 232, 232);
- 79 /// <summary>
- 80 /// 分隔偶数栏配景色
- 81 /// </summary>
- 82 /// <value>The color of the split even.</value>
- 83 [Browsable(true)]
- 84 [Category("自定义")]
- 85 [Description("获取或设置分隔偶数栏配景色")]
- 86 public Color SplitEvenColor
- 87 {
- 88 get { return splitEvenColor; }
- 89 set { splitEvenColor = value; }
- 90 }
- 91
- 92 /// <summary>
- 93 /// The line color
- 94 /// </summary>
- 95 private Color lineColor = Color.FromArgb(153, 153, 153);
- 96 /// <summary>
- 97 /// Gets or sets the color of the line.
- 98 /// </summary>
- 99 /// <value>The color of the line.</value>
- 100 [Browsable(true)]
- 101 [Category("自定义")]
- 102 [Description("获取或设置线条色")]
- 103 public Color LineColor
- 104 {
- 105 get { return lineColor; }
- 106 set
- 107 {
- 108 lineColor = value;
- 109 Invalidate();
- 110 }
- 111 }
- 112
- 113 /// <summary>
- 114 /// The radar positions
- 115 /// </summary>
- 116 private RadarPosition[] radarPositions;
- 117 /// <summary>
- 118 /// 节点列表,至少必要3个
- 119 /// </summary>
- 120 /// <value>The radar positions.</value>
- 121 [Browsable(true)]
- 122 [Category("自定义")]
- 123 [Description("获取或设置节点,至少必要3个")]
- 124 public RadarPosition[] RadarPositions
- 125 {
- 126 get { return radarPositions; }
- 127 set
- 128 {
- 129 radarPositions = value;
- 130 Invalidate();
- 131 }
- 132 }
- 133
- 134 /// <summary>
- 135 /// The title
- 136 /// </summary>
- 137 private string title;
- 138 /// <summary>
- 139 /// 标题
- 140 /// </summary>
- 141 /// <value>The title.</value>
- 142 [Browsable(true)]
- 143 [Category("自定义")]
- 144 [Description("获取或设置标题")]
- 145 public string Title
- 146 {
- 147 get { return title; }
- 148 set
- 149 {
- 150 title = value;
- 151 ResetTitleSize();
- 152 Invalidate();
- 153 }
- 154 }
- 155
- 156 /// <summary>
- 157 /// The title font
- 158 /// </summary>
- 159 private Font titleFont = new Font("微软雅黑", 12);
- 160 /// <summary>
- 161 /// Gets or sets the title font.
- 162 /// </summary>
- 163 /// <value>The title font.</value>
- 164 [Browsable(true)]
- 165 [Category("自定义")]
- 166 [Description("获取或设置标题字体")]
- 167 public Font TitleFont
- 168 {
- 169 get { return titleFont; }
- 170 set
- 171 {
- 172 titleFont = value;
- 173 ResetTitleSize();
- 174 Invalidate();
- 175 }
- 176 }
- 177
- 178 /// <summary>
- 179 /// The title color
- 180 /// </summary>
- 181 private Color titleColor = Color.Black;
- 182 /// <summary>
- 183 /// Gets or sets the color of the title.
- 184 /// </summary>
- 185 /// <value>The color of the title.</value>
- 186 [Browsable(true)]
- 187 [Category("自定义")]
- 188 [Description("获取或设置标题文本颜色")]
- 189 public Color TitleColor
- 190 {
- 191 get { return titleColor; }
- 192 set
- 193 {
- 194 titleColor = value;
- 195 Invalidate();
- 196 }
- 197 }
- 198
- 199 /// <summary>
- 200 /// The lines
- 201 /// </summary>
- 202 private RadarLine[] lines;
- 203 /// <summary>
- 204 /// Gets or sets the lines.
- 205 /// </summary>
- 206 /// <value>The lines.</value>
- 207 [Browsable(true)]
- 208 [Category("自定义")]
- 209 [Description("获取或设置值线条,Values长度必须与RadarPositions长度一致,否则无法显示")]
- 210 public RadarLine[] Lines
- 211 {
- 212 get { return lines; }
- 213 set
- 214 {
- 215 lines = value;
- 216 Invalidate();
- 217 }
- 218 }
- 219
- 220
- 221 /// <summary>
- 222 /// The title size
- 223 /// </summary>
- 224 SizeF titleSize = SizeF.Empty;
- 225 /// <summary>
- 226 /// The m rect working
- 227 /// </summary>
- 228 private RectangleF m_rectWorking = Rectangle.Empty;
- 229 /// <summary>
- 230 /// The line value type size
- 231 /// </summary>
- 232 SizeF lineValueTypeSize = SizeF.Empty;
- 233 /// <summary>
- 234 /// The int line value COM count
- 235 /// </summary>
- 236 int intLineValueComCount = 0;
- 237 /// <summary>
- 238 /// The int line value row count
- 239 /// </summary>
- 240 int intLineValueRowCount = 0;
- 241 /// <summary>
- 242 /// Initializes a new instance of the <see cref="UCRadarChart"/> class.
- 243 /// </summary>
- 244 public UCRadarChart()
- 245 {
- 246 this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
- 247 this.SetStyle(ControlStyles.DoubleBuffer, true);
- 248 this.SetStyle(ControlStyles.ResizeRedraw, true);
- 249 this.SetStyle(ControlStyles.Selectable, true);
- 250 this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
- 251 this.SetStyle(ControlStyles.UserPaint, true);
- 252 this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
- 253 this.SizeChanged += UCRadarChart_SizeChanged;
- 254 Size = new System.Drawing.Size(150, 150);
- 255 radarPositions = new RadarPosition[0];
- 256 if (ControlHelper.IsDesignMode())
- 257 {
- 258 radarPositions = new RadarPosition[6];
- 259 for (int i = 0; i < 6; i++)
- 260 {
- 261 radarPositions[i] = new RadarPosition
- 262 {
- 263 Text = "Item" + (i + 1),
- 264 MaxValue = 100
- 265 };
- 266 }
- 267 }
- 268
- 269 lines = new RadarLine[0];
- 270 if (ControlHelper.IsDesignMode())
- 271 {
- 272 Random r = new Random();
- 273 lines = new RadarLine[2];
- 274 for (int i = 0; i < 2; i++)
- 275 {
- 276 lines[i] = new RadarLine()
- 277 {
- 278 Name = "line" + i
- 279 };
- 280 lines[i].Values = new double[radarPositions.Length];
- 281 for (int j = 0; j < radarPositions.Length; j++)
- 282 {
- 283 lines[i].Values[j] = r.Next(20, (int)radarPositions[j].MaxValue);
- 284 }
- 285 }
- 286 }
- 287 }
- 288
- 289 /// <summary>
- 290 /// Handles the SizeChanged event of the UCRadarChart control.
- 291 /// </summary>
- 292 /// <param name="sender">The source of the event.</param>
- 293 /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
- 294 void UCRadarChart_SizeChanged(object sender, EventArgs e)
- 295 {
- 296 ResetWorkingRect();
- 297 }
- 298
- 299 /// <summary>
- 300 /// Resets the working rect.
- 301 /// </summary>
- 302 private void ResetWorkingRect()
- 303 {
- 304 if (lines != null && lines.Length > 0)
- 305 {
- 306 using (Graphics g = this.CreateGraphics())
- 307 {
- 308 foreach (var item in lines)
- 309 {
- 310 var s = g.MeasureString(item.Name, Font);
- 311 if (s.Width > lineValueTypeSize.Width)
- 312 lineValueTypeSize = s;
- 313 }
- 314 }
- 315 }
- 316 var lineTypePanelHeight = 0f;
- 317 if (lineValueTypeSize != SizeF.Empty)
- 318 {
- 319 intLineValueComCount = (int)(this.Width / (lineValueTypeSize.Width + 25));
- 320
- 321 intLineValueRowCount = lines.Length / intLineValueComCount;
- 322 if (lines.Length % intLineValueComCount != 0)
- 323 {
- 324 intLineValueRowCount++;
- 325 }
- 326 lineTypePanelHeight = (lineValueTypeSize.Height + 10) * intLineValueRowCount;
- 327 }
- 328 var min = Math.Min(this.Width, this.Height - titleSize.Height - lineTypePanelHeight);
- 329 var rectWorking = new RectangleF((this.Width - min) / 2 + 10, titleSize.Height + lineTypePanelHeight + 10, min - 10, min - 10);
- 330 //处置惩罚文字
- 331 float fltSplitAngle = 360F / radarPositions.Length;
- 332 float fltRadiusWidth = rectWorking.Width / 2;
- 333 float minX = rectWorking.Left;
- 334 float maxX = rectWorking.Right;
- 335 float minY = rectWorking.Top;
- 336 float maxY = rectWorking.Bottom;
- 337 using (Graphics g = this.CreateGraphics())
- 338 {
- 339 PointF centrePoint = new PointF(rectWorking.Left + rectWorking.Width / 2, rectWorking.Top + rectWorking.Height / 2);
- 340 for (int i = 0; i < radarPositions.Length; i++)
- 341 {
- 342 float fltAngle = 270 + fltSplitAngle * i;
- 343 fltAngle = fltAngle % 360;
- 344 PointF _point = GetPointByAngle(centrePoint, fltAngle, fltRadiusWidth);
- 345 var _txtSize = g.MeasureString(radarPositions[i].Text, Font);
- 346 if (_point.X < centrePoint.X)//左
- 347 {
- 348 if (_point.X - _txtSize.Width < minX)
- 349 {
- 350 minX = rectWorking.Left + _txtSize.Width;
- 351 }
- 352 }
- 353 else//右
- 354 {
- 355 if (_point.X + _txtSize.Width > maxX)
- 356 {
- 357 maxX = rectWorking.Right - _txtSize.Width;
- 358 }
- 359 }
- 360 if (_point.Y < centrePoint.Y)//上
- 361 {
- 362 if (_point.Y - _txtSize.Height < minY)
- 363 {
- 364 minY = rectWorking.Top + _txtSize.Height;
- 365 }
- 366 }
- 367 else//下
- 368 {
- 369 if (_point.Y + _txtSize.Height > maxY)
- 370 {
- 371 maxY = rectWorking.Bottom - _txtSize.Height;
- 372 }
- 373 }
- 374 }
- 375 }
- 376
- 377 min = Math.Min(maxX - minX, maxY - minY);
- 378 m_rectWorking = new RectangleF(minX, minY, min, min);
- 379 }
- 380
- 381 /// <summary>
- 382 /// 引发 <see cref="E:System.Windows.Forms.Control.Paint" /> 变乱。
- 383 /// </summary>
- 384 /// <param name="e">包罗变乱数据的 <see cref="T:System.Windows.Forms.PaintEventArgs" />。</param>
- 385 protected override void OnPaint(PaintEventArgs e)
- 386 {
- 387 base.OnPaint(e);
- 388 var g = e.Graphics;
- 389 g.SetGDIHigh();
- 390
- 391 if (!string.IsNullOrEmpty(title))
- 392 {
- 393 g.DrawString(title, titleFont, new SolidBrush(titleColor), new RectangleF(m_rectWorking.Left + (m_rectWorking.Width - titleSize.Width) / 2, m_rectWorking.Top - titleSize.Height - 10 - (intLineValueRowCount * (10 + lineValueTypeSize.Height)), titleSize.Width, titleSize.Height));
- 394 }
- 395
- 396 if (radarPositions.Length <= 2)
- 397 {
- 398 g.DrawString("至少必要3个极点", Font, new SolidBrush(Color.Black), m_rectWorking, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
- 399 return;
- 400 }
- 401
- 402 var y = m_rectWorking.Top - 20 - (intLineValueRowCount * (10 + lineValueTypeSize.Height));
- 403
- 404 for (int i = 0; i < intLineValueRowCount; i++)
- 405 {
- 406 var x = 0f;
- 407 int intCount = intLineValueComCount;
- 408 if (i == intLineValueRowCount - 1)
- 409 {
- 410 intCount = lines.Length % intLineValueComCount;
- 411
- 412 }
- 413 x = m_rectWorking.Left + (m_rectWorking.Width - intCount * (lineValueTypeSize.Width + 25)) / 2;
- 414
- 415 for (int j = 0; j < intCount; j++)
- 416 {
- 417 g.FillRectangle(new SolidBrush(lines[i * intLineValueComCount + j].LineColor.Value), new RectangleF(x + (lineValueTypeSize.Width + 25)*j, y + lineValueTypeSize.Height * i, 15, lineValueTypeSize.Height));
- 418 g.DrawString(lines[i * intLineValueComCount + j].Name, Font, new SolidBrush(lines[i * intLineValueComCount + j].LineColor.Value), new PointF(x + (lineValueTypeSize.Width + 25) * j + 20, y + lineValueTypeSize.Height * i));
- 419 }
- 420 }
- 421
- 422 float fltSplitAngle = 360F / radarPositions.Length;
- 423 float fltRadiusWidth = m_rectWorking.Width / 2;
- 424 float fltSplitRadiusWidth = fltRadiusWidth / splitCount;
- 425 PointF centrePoint = new PointF(m_rectWorking.Left + m_rectWorking.Width / 2, m_rectWorking.Top + m_rectWorking.Height / 2);
- 426
- 427 List<List<PointF>> lstRingPoints = new List<List<PointF>>(splitCount);
- 428 //分割点
- 429 for (int i = 0; i < radarPositions.Length; i++)
- 430 {
- 431 float fltAngle = 270 + fltSplitAngle * i;
- 432 fltAngle = fltAngle % 360;
- 433 for (int j = 0; j < splitCount; j++)
- 434 {
- 435 if (i == 0)
- 436 {
- 437 lstRingPoints.Add(new List<PointF>());
- 438 }
- 439 PointF _point = GetPointByAngle(centrePoint, fltAngle, fltSplitRadiusWidth * (splitCount - j));
- 440 lstRingPoints[j].Add(_point);
- 441 }
- 442 }
- 443
- 444 for (int i = 0; i < lstRingPoints.Count; i++)
- 445 {
- 446 var ring = lstRingPoints[i];
- 447 GraphicsPath path = new GraphicsPath();
- 448 path.AddLines(ring.ToArray());
- 449 if ((lstRingPoints.Count - i) % 2 == 0)
- 450 {
- 451 g.FillPath(new SolidBrush(splitEvenColor), path);
- 452 }
- 453 else
- 454 {
- 455 g.FillPath(new SolidBrush(splitOddColor), path);
- 456 }
- 457 }
- 458
- 459 //画环
- 460 foreach (var ring in lstRingPoints)
- 461 {
- 462 ring.Add(ring[0]);
- 463 g.DrawLines(new Pen(new SolidBrush(lineColor)), ring.ToArray());
- 464 }
- 465 //分割线
- 466 foreach (var item in lstRingPoints[0])
- 467 {
- 468 g.DrawLine(new Pen(new SolidBrush(lineColor)), centrePoint, item);
- 469 }
- 470
- 471 //值
- 472 for (int i = 0; i < lines.Length; i++)
- 473 {
- 474 var line = lines[i];
- 475 if (line.Values.Length != radarPositions.Length)//如果数据长度和节点长度不一致则不绘制
- 476 continue;
- 477 if (line.LineColor == null || line.LineColor == Color.Empty || line.LineColor == Color.Transparent)
- 478 line.LineColor = ControlHelper.Colors[i + 13];
- 479 List<PointF> ps = new List<PointF>();
- 480 for (int j = 0; j < radarPositions.Length; j++)
- 481 {
- 482 float fltAngle = 270 + fltSplitAngle * j;
- 483 fltAngle = fltAngle % 360;
- 484 PointF _point = GetPointByAngle(centrePoint, fltAngle, fltRadiusWidth * (float)(line.Values[j] / radarPositions[i].MaxValue));
- 485 ps.Add(_point);
- 486 }
- 487 ps.Add(ps[0]);
- 488 if (line.FillColor != null && line.FillColor != Color.Empty && line.FillColor != Color.Transparent)
- 489 {
- 490 GraphicsPath path = new GraphicsPath();
- 491 path.AddLines(ps.ToArray());
- 492 g.FillPath(new SolidBrush(line.FillColor.Value), path);
- 493 }
- 494 g.DrawLines(new Pen(new SolidBrush(line.LineColor.Value), 2), ps.ToArray());
- 495
- 496 for (int j = 0; j < radarPositions.Length; j++)
- 497 {
- 498 var item = ps[j];
- 499 g.FillEllipse(new SolidBrush(Color.White), new RectangleF(item.X - 3, item.Y - 3, 6, 6));
- 500 g.DrawEllipse(new Pen(new SolidBrush(line.LineColor.Value)), new RectangleF(item.X - 3, item.Y - 3, 6, 6));
- 501 if (line.ShowValueText)
- 502 {
- 503 var valueSize = g.MeasureString(line.Values[j].ToString("0.##"), Font);
- 504 g.DrawString(line.Values[j].ToString("0.##"), Font, new SolidBrush(line.LineColor.Value), new PointF(item.X - valueSize.Width / 2, item.Y - valueSize.Height - 5));
- 505 }
- 506 }
- 507 }
- 508
- 509 //文本
- 510
- 511 for (int i = 0; i < radarPositions.Length; i++)
- 512 {
- 513 PointF point = lstRingPoints[0][i];
- 514 var txtSize = g.MeasureString(radarPositions[i].Text, Font);
- 515
- 516 if (point.X == centrePoint.X)
- 517 {
- 518 if (point.Y > centrePoint.Y)
- 519 {
- 520 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - txtSize.Width / 2, point.Y + 10));
- 521 }
- 522 else
- 523 {
- 524 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - txtSize.Width / 2, point.Y - 10 - txtSize.Height));
- 525 }
- 526 }
- 527 else if (point.Y == centrePoint.Y)
- 528 {
- 529 if (point.X < centrePoint.X)
- 530 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - 10 - txtSize.Width, point.Y - txtSize.Height / 2));
- 531 else
- 532 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + 10, point.Y - txtSize.Height / 2));
- 533 }
- 534 else if (point.X < centrePoint.X)//左
- 535 {
- 536 if (point.Y < centrePoint.Y)//左上
- 537 {
- 538 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - 10 - txtSize.Width, point.Y - 10 + txtSize.Height / 2));
- 539 }
- 540 else//左下
- 541 {
- 542 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - 10 - txtSize.Width, point.Y + 10 - txtSize.Height / 2));
- 543 }
- 544 }
- 545 else
- 546 {
- 547 if (point.Y < centrePoint.Y)//右上
- 548 {
- 549 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + 10, point.Y - 10 + txtSize.Height / 2));
- 550 }
- 551 else//右下
- 552 {
- 553 g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + 10, point.Y + 10 - txtSize.Height / 2));
- 554 }
- 555 }
- 556 }
- 557
- 558 }
- 559
- 560 #region 根据中心点、角度、半径盘算圆边坐标点 English:Calculating the coordinate points of circular edge according to the center point, angle and radius
- 561 /// <summary>
- 562 /// 功能形貌:根据中心点、角度、半径盘算圆边坐标点 English:Calculating the coordinate points of circular edge according to the center point, angle and radius
- 563 /// 作 者:HZH
- 564 /// 创建日期:2019-09-25 09:46:32
- 565 /// 任务编号:POS
- 566 /// </summary>
- 567 /// <param name="centrePoint">centrePoint</param>
- 568 /// <param name="fltAngle">fltAngle</param>
- 569 /// <param name="fltRadiusWidth">fltRadiusWidth</param>
- 570 /// <returns>返回值</returns>
- 571 private PointF GetPointByAngle(PointF centrePoint, float fltAngle, float fltRadiusWidth)
- 572 {
- 573 PointF p = centrePoint;
- 574 if (fltAngle == 0)
- 575 {
- 576 p.X += fltRadiusWidth;
- 577 }
- 578 else if (fltAngle == 90)
- 579 {
- 580 p.Y += fltRadiusWidth;
- 581 }
- 582 else if (fltAngle == 180)
- 583 {
- 584 p.X -= fltRadiusWidth;
- 585 }
- 586 else if (fltAngle == 270)
- 587 {
- 588 p.Y -= fltRadiusWidth;
- 589 }
- 590 else if (fltAngle > 0 && fltAngle < 90)
- 591 {
- 592 p.Y += (float)Math.Sin(Math.PI * (fltAngle / 180.00F)) * fltRadiusWidth;
- 593 p.X += (float)Math.Cos(Math.PI * (fltAngle / 180.00F)) * fltRadiusWidth;
- 594 }
- 595 else if (fltAngle > 90 && fltAngle < 180)
- 596 {
- 597 p.Y += (float)Math.Sin(Math.PI * ((180 - fltAngle) / 180.00F)) * fltRadiusWidth;
- 598 p.X -= (float)Math.Cos(Math.PI * ((180 - fltAngle) / 180.00F)) * fltRadiusWidth;
- 599 }
- 600 else if (fltAngle > 180 && fltAngle < 270)
- 601 {
- 602 p.Y -= (float)Math.Sin(Math.PI * ((fltAngle - 180) / 180.00F)) * fltRadiusWidth;
- 603 p.X -= (float)Math.Cos(Math.PI * ((fltAngle - 180) / 180.00F)) * fltRadiusWidth;
- 604 }
- 605 else if (fltAngle > 270 && fltAngle < 360)
- 606 {
- 607 p.Y -= (float)Math.Sin(Math.PI * ((360 - fltAngle) / 180.00F)) * fltRadiusWidth;
- 608 p.X += (float)Math.Cos(Math.PI * ((360 - fltAngle) / 180.00F)) * fltRadiusWidth;
- 609 }
- 610 return p;
- 611 }
- 612 #endregion
- 613
- 614 /// <summary>
- 615 /// Resets the size of the title.
- 616 /// </summary>
- 617 private void ResetTitleSize()
- 618 {
- 619 if (!string.IsNullOrEmpty(title))
- 620 {
- 621 using (Graphics g = this.CreateGraphics())
- 622 {
- 623 titleSize = g.MeasureString(title, titleFont);
- 624 }
- 625 }
- 626 else
- 627 {
- 628 titleSize = SizeF.Empty;
- 629 }
- 630 titleSize.Height += 20;
- 631 ResetWorkingRect();
- 632 }
- 633 }
- 634 }
复制代码
View Code
末了的话
如果你喜好的话,请到 https://gitee.com/kwwwvagaa/net_winform_custom_control 点个星星吧
来源:https://www.cnblogs.com/bfyx/archive/2019/09/25/11584514.html |