(七十二)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 支持一下吧

欢迎前来交流探讨: 企鹅群568015492 技术图片

麻烦博客下方点个【推荐】,谢谢

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 
View Code

 

最后的话

如果你喜欢的话,请到 https://gitee.com/kwwwvagaa/net_winform_custom_control 点个星星吧

以上是关于(七十二)c#Winform自定义控件-雷达图的主要内容,如果未能解决你的问题,请参考以下文章

(六十二)c#Winform自定义控件-警灯(工业)

(五十二)c#Winform自定义控件-LED数字

Android自定义控件系列—Button七十二变

C# Winform自定义控件加载到主窗体中之后,自定控件上面的空间 怎么和主窗体上其他控件交互?

Android自定义控件 芝麻信用分雷达图

雷达分析图控件----------WinForm控件开发系列