(七十二)c#Winform自定义控件-雷达图
Posted bfyx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(七十二)c#Winform自定义控件-雷达图相关的知识,希望对你有一定的参考价值。
前提
入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。
GitHub:https://github.com/kwwwvagaa/NetWinformControl
码云:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
如果觉得写的还行,请点个 star 支持一下吧
麻烦博客下方点个【推荐】,谢谢
NuGet
Install-Package HZH_Controls
目录
https://www.cnblogs.com/bfyx/p/11364884.html
用处及效果
准备工作
GDI+画的,不会的可以先百度了解下
开始
添加一个类UCRadarChart ,继承 UserControl
添加一些控制属性
1 /// <summary> 2 /// The split count 3 /// </summary> 4 private int splitCount = 5; 5 /// <summary> 6 /// Gets or sets the split count. 7 /// </summary> 8 /// <value>The split count.</value> 9 [Browsable(true)] 10 [Category("自定义")] 11 [Description("获取或设置分隔份数")] 12 public int SplitCount 13 14 get return splitCount; 15 set 16 17 splitCount = value; 18 Invalidate(); 19 20 21 22 /// <summary> 23 /// The split odd color 24 /// </summary> 25 private Color splitOddColor = Color.White; 26 /// <summary> 27 /// 分隔奇数栏背景色 28 /// </summary> 29 /// <value>The color of the split odd.</value> 30 [Browsable(true)] 31 [Category("自定义")] 32 [Description("获取或设置分隔奇数栏背景色")] 33 public Color SplitOddColor 34 35 get return splitOddColor; 36 set 37 38 splitOddColor = value; 39 Invalidate(); 40 41 42 /// <summary> 43 /// The split even color 44 /// </summary> 45 private Color splitEvenColor = Color.FromArgb(232, 232, 232); 46 /// <summary> 47 /// 分隔偶数栏背景色 48 /// </summary> 49 /// <value>The color of the split even.</value> 50 [Browsable(true)] 51 [Category("自定义")] 52 [Description("获取或设置分隔偶数栏背景色")] 53 public Color SplitEvenColor 54 55 get return splitEvenColor; 56 set splitEvenColor = value; 57 58 59 /// <summary> 60 /// The line color 61 /// </summary> 62 private Color lineColor = Color.FromArgb(153, 153, 153); 63 /// <summary> 64 /// Gets or sets the color of the line. 65 /// </summary> 66 /// <value>The color of the line.</value> 67 [Browsable(true)] 68 [Category("自定义")] 69 [Description("获取或设置线条色")] 70 public Color LineColor 71 72 get return lineColor; 73 set 74 75 lineColor = value; 76 Invalidate(); 77 78 79 80 /// <summary> 81 /// The radar positions 82 /// </summary> 83 private RadarPosition[] radarPositions; 84 /// <summary> 85 /// 节点列表,至少需要3个 86 /// </summary> 87 /// <value>The radar positions.</value> 88 [Browsable(true)] 89 [Category("自定义")] 90 [Description("获取或设置节点,至少需要3个")] 91 public RadarPosition[] RadarPositions 92 93 get return radarPositions; 94 set 95 96 radarPositions = value; 97 Invalidate(); 98 99 100 101 /// <summary> 102 /// The title 103 /// </summary> 104 private string title; 105 /// <summary> 106 /// 标题 107 /// </summary> 108 /// <value>The title.</value> 109 [Browsable(true)] 110 [Category("自定义")] 111 [Description("获取或设置标题")] 112 public string Title 113 114 get return title; 115 set 116 117 title = value; 118 ResetTitleSize(); 119 Invalidate(); 120 121 122 123 /// <summary> 124 /// The title font 125 /// </summary> 126 private Font titleFont = new Font("微软雅黑", 12); 127 /// <summary> 128 /// Gets or sets the title font. 129 /// </summary> 130 /// <value>The title font.</value> 131 [Browsable(true)] 132 [Category("自定义")] 133 [Description("获取或设置标题字体")] 134 public Font TitleFont 135 136 get return titleFont; 137 set 138 139 titleFont = value; 140 ResetTitleSize(); 141 Invalidate(); 142 143 144 145 /// <summary> 146 /// The title color 147 /// </summary> 148 private Color titleColor = Color.Black; 149 /// <summary> 150 /// Gets or sets the color of the title. 151 /// </summary> 152 /// <value>The color of the title.</value> 153 [Browsable(true)] 154 [Category("自定义")] 155 [Description("获取或设置标题文本颜色")] 156 public Color TitleColor 157 158 get return titleColor; 159 set 160 161 titleColor = value; 162 Invalidate(); 163 164 165 166 /// <summary> 167 /// The lines 168 /// </summary> 169 private RadarLine[] lines; 170 /// <summary> 171 /// Gets or sets the lines. 172 /// </summary> 173 /// <value>The lines.</value> 174 [Browsable(true)] 175 [Category("自定义")] 176 [Description("获取或设置值线条,Values长度必须与RadarPositions长度一致,否则无法显示")] 177 public RadarLine[] Lines 178 179 get return lines; 180 set 181 182 lines = value; 183 Invalidate(); 184 185 186 187 188 /// <summary> 189 /// The title size 190 /// </summary> 191 SizeF titleSize = SizeF.Empty; 192 /// <summary> 193 /// The m rect working 194 /// </summary> 195 private RectangleF m_rectWorking = Rectangle.Empty; 196 /// <summary> 197 /// The line value type size 198 /// </summary> 199 SizeF lineValueTypeSize = SizeF.Empty; 200 /// <summary> 201 /// The int line value COM count 202 /// </summary> 203 int intLineValueComCount = 0; 204 /// <summary> 205 /// The int line value row count 206 /// </summary> 207 int intLineValueRowCount = 0;
属性改变时处理工作区域
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
最后的话
如果你喜欢的话,请到 https://gitee.com/kwwwvagaa/net_winform_custom_control 点个星星吧
以上是关于(七十二)c#Winform自定义控件-雷达图的主要内容,如果未能解决你的问题,请参考以下文章