ibcadmin 发表于 2019-9-26 09:17:42

c#Winform自定义控件-雷达图

<h1>条件</h1>
<p>入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。</p>
<p>GitHub:https://github.com/kwwwvagaa/NetWinformControl</p>
<p>码云:https://gitee.com/kwwwvagaa/net_winform_custom_control.git</p>
<p>如果觉得写的还行,请点个 star 支持一下吧</p>
<p>接待前来互换探究: 企鹅群568015492 <div align="center"></div></p>
<p>麻烦博客下方点个【保举】,谢谢</p>
<h1>NuGet</h1>

Install-Package HZH_Controls

<h1>目次</h1>
<p>https://www.cnblogs.com/bfyx/p/11364884.html</p>
<h1>用处及效果</h1>
<p><div align="center"></div></p>
<h1>预备工作</h1>
<p>GDI+画的,不会的可以先百度了解下</p>
<h1>开始</h1>
<p>添加一个类UCRadarChart ,继承 UserControl</p>
<p>添加一些控制属性</p>

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         
10         
11         
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         
31         
32         
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         
51         
52         
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         
68         
69         
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         
89         
90         
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         
110         
111         
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         
132         
133         
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         
154         
155         
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         
175         
176         
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;

<p>属性改变时处置惩罚工作区域</p>

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.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         }

<p>重绘</p>

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.LineColor.Value), new RectangleF(x + (lineValueTypeSize.Width + 25)*j, y + lineValueTypeSize.Height * i, 15, lineValueTypeSize.Height));
34                     g.DrawString(lines.Name, Font, new SolidBrush(lines.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.Add(_point);
57               }
58             }
59
60             for (int i = 0; i < lstRingPoints.Count; i++)
61             {
62               var ring = lstRingPoints;
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);
79               g.DrawLines(new Pen(new SolidBrush(lineColor)), ring.ToArray());
80             }
81             //分割线
82             foreach (var item in lstRingPoints)
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;
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;
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 / radarPositions.MaxValue));
101                     ps.Add(_point);
102               }
103               ps.Add(ps);
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;
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.ToString("0.##"), Font);
120                         g.DrawString(line.Values.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;
130               var txtSize = g.MeasureString(radarPositions.Text, Font);
131
132               if (point.X == centrePoint.X)
133               {
134                     if (point.Y > centrePoint.Y)
135                     {
136                         g.DrawString(radarPositions.Text, Font, new SolidBrush(ForeColor), new PointF(point.X - txtSize.Width / 2, point.Y + 10));
137                     }
138                     else
139                     {
140                         g.DrawString(radarPositions.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.Text, Font, new SolidBrush(ForeColor), new PointF(point.X - 10 - txtSize.Width, point.Y - txtSize.Height / 2));
147                     else
148                         g.DrawString(radarPositions.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.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.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.Text, Font, new SolidBrush(ForeColor), new PointF(point.X + 10, point.Y - 10 + txtSize.Height / 2));
166                     }
167                     else//右下
168                     {
169                         g.DrawString(radarPositions.Text, Font, new SolidBrush(ForeColor), new PointF(point.X + 10, point.Y + 10 - txtSize.Height / 2));
170                     }
171               }
172             }
173
174         }

<p>辅助函数</p>

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         }

<p>完整代码</p>
<div align="center"></div><div align="center"></div>

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         
43         
44         
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         
64         
65         
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         
84         
85         
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         
101         
102         
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         
122         
123         
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         
143         
144         
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         
165         
166         
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         
187         
188         
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         
208         
209         
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;
256             if (ControlHelper.IsDesignMode())
257             {
258               radarPositions = new RadarPosition;
259               for (int i = 0; i < 6; i++)
260               {
261                     radarPositions = new RadarPosition
262                     {
263                         Text = "Item" + (i + 1),
264                         MaxValue = 100
265                     };
266               }
267             }
268
269             lines = new RadarLine;
270             if (ControlHelper.IsDesignMode())
271             {
272               Random r = new Random();
273               lines = new RadarLine;
274               for (int i = 0; i < 2; i++)
275               {
276                     lines = new RadarLine()
277                     {
278                         Name = "line" + i
279                     };
280                     lines.Values = new double;
281                     for (int j = 0; j < radarPositions.Length; j++)
282                     {
283                         lines.Values = r.Next(20, (int)radarPositions.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.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.LineColor.Value), new RectangleF(x + (lineValueTypeSize.Width + 25)*j, y + lineValueTypeSize.Height * i, 15, lineValueTypeSize.Height));
418                     g.DrawString(lines.Name, Font, new SolidBrush(lines.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.Add(_point);
441               }
442             }
443
444             for (int i = 0; i < lstRingPoints.Count; i++)
445             {
446               var ring = lstRingPoints;
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);
463               g.DrawLines(new Pen(new SolidBrush(lineColor)), ring.ToArray());
464             }
465             //分割线
466             foreach (var item in lstRingPoints)
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;
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;
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 / radarPositions.MaxValue));
485                     ps.Add(_point);
486               }
487               ps.Add(ps);
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;
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.ToString("0.##"), Font);
504                         g.DrawString(line.Values.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;
514               var txtSize = g.MeasureString(radarPositions.Text, Font);
515
516               if (point.X == centrePoint.X)
517               {
518                     if (point.Y > centrePoint.Y)
519                     {
520                         g.DrawString(radarPositions.Text, Font, new SolidBrush(ForeColor), new PointF(point.X - txtSize.Width / 2, point.Y + 10));
521                     }
522                     else
523                     {
524                         g.DrawString(radarPositions.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.Text, Font, new SolidBrush(ForeColor), new PointF(point.X - 10 - txtSize.Width, point.Y - txtSize.Height / 2));
531                     else
532                         g.DrawString(radarPositions.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.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.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.Text, Font, new SolidBrush(ForeColor), new PointF(point.X + 10, point.Y - 10 + txtSize.Height / 2));
550                     }
551                     else//右下
552                     {
553                         g.DrawString(radarPositions.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
<p> </p>
<h1>末了的话</h1>
<p>如果你喜好的话,请到 https://gitee.com/kwwwvagaa/net_winform_custom_control 点个星星吧</p><br><br/><br/><br/><br/><br/>来源:<a href="https://www.cnblogs.com/bfyx/archive/2019/09/25/11584514.html" target="_blank">https://www.cnblogs.com/bfyx/archive/2019/09/25/11584514.html</a>
页: [1]
查看完整版本: c#Winform自定义控件-雷达图