欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

(七十二)c#Winform自定义控件-雷达图

程序员文章站 2023-10-16 22:54:41
前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。 GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:https://gitee.com/kwwwvagaa/net_winform_custom_contr ......

前提

入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。

github:https://github.com/kwwwvagaa/netwinformcontrol

码云:

如果觉得写的还行,请点个 star 支持一下吧

欢迎前来交流探讨: 企鹅群568015492 

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

nuget

install-package hzh_controls

目录

用处及效果

(七十二)c#Winform自定义控件-雷达图

准备工作

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 }

 

最后的话

如果你喜欢的话,请到  点个星星吧