标签:ram date() rect format data 准备 win 继承 用处
前提
入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。
GitHub:https://github.com/kwwwvagaa/NetWinformControl
码云:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
如果觉得写的还行,请点个 star 支持一下吧
欢迎前来交流探讨: 企鹅群568015492
麻烦博客下方点个【推荐】,谢谢
NuGet
Install-Package HZH_Controls
目录
https://www.cnblogs.com/bfyx/p/11364884.html
用处及效果
准备工作
GDI+画的,不会的可以先百度了解下
开始
添加一个类UCRadarChart ,继承 UserControl
添加一些控制属性
1 ///
2 /// The split count
3 ///
4 private int splitCount = 5;
5 ///
6 /// Gets or sets the split count.
7 ///
8 /// The split count.
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 ///
23 /// The split odd color
24 ///
25 private Color splitOddColor = Color.White;
26 ///
27 /// 分隔奇数栏背景色
28 ///
29 /// The color of the split odd.
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 ///
43 /// The split even color
44 ///
45 private Color splitEvenColor = Color.FromArgb(232, 232, 232);
46 ///
47 /// 分隔偶数栏背景色
48 ///
49 /// The color of the split even.
50 [Browsable(true)]
51 [Category("自定义")]
52 [Description("获取或设置分隔偶数栏背景色")]
53 public Color SplitEvenColor
54 {
55 get { return splitEvenColor; }
56 set { splitEvenColor = value; }
57 }
58
59 ///
60 /// The line color
61 ///
62 private Color lineColor = Color.FromArgb(153, 153, 153);
63 ///
64 /// Gets or sets the color of the line.
65 ///
66 /// The color of the line.
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 ///
81 /// The radar positions
82 ///
83 private RadarPosition[] radarPositions;
84 ///
85 /// 节点列表,至少需要3个
86 ///
87 /// The radar positions.
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 ///
102 /// The title
103 ///
104 private string title;
105 ///
106 /// 标题
107 ///
108 /// The title.
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 ///
124 /// The title font
125 ///
126 private Font titleFont = new Font("微软雅黑", 12);
127 ///
128 /// Gets or sets the title font.
129 ///
130 /// The title font.
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 ///
146 /// The title color
147 ///
148 private Color titleColor = Color.Black;
149 ///
150 /// Gets or sets the color of the title.
151 ///
152 /// The color of the title.
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 ///
167 /// The lines
168 ///
169 private RadarLine[] lines;
170 ///
171 /// Gets or sets the lines.
172 ///
173 /// The lines.
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 ///
189 /// The title size
190 ///
191 SizeF titleSize = SizeF.Empty;
192 ///
193 /// The m rect working
194 ///
195 private RectangleF m_rectWorking = Rectangle.Empty;
196 ///
197 /// The line value type size
198 ///
199 SizeF lineValueTypeSize = SizeF.Empty;
200 ///
201 /// The int line value COM count
202 ///
203 int intLineValueComCount = 0;
204 ///
205 /// The int line value row count
206 ///
207 int intLineValueRowCount = 0;
属性改变时处理工作区域
1 ///
2 /// Handles the SizeChanged event of the UCRadarChart control.
3 ///
4 /// The source of the event.
5 /// The instance containing the event data.
6 void UCRadarChart_SizeChanged(object sender, EventArgs e)
7 {
8 ResetWorkingRect();
9 }
10
11 ///
12 /// Resets the working rect.
13 ///
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 )
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 //左
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 //上
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 )
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 )
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> lstRingPoints = new List>(splitCount);
44 //分割点
45 for (int i = 0; i )
46 {
47 float fltAngle = 270 + fltSplitAngle * i;
48 fltAngle = fltAngle % 360;
49 for (int j = 0; j )
50 {
51 if (i == 0)
52 {
53 lstRingPoints.Add(new List());
54 }
55 PointF _point = GetPointByAngle(centrePoint, fltAngle, fltSplitRadiusWidth * (splitCount - j));
56 lstRingPoints[j].Add(_point);
57 }
58 }
59
60 for (int i = 0; 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 )
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 ps = new List();
96 for (int j = 0; 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 )
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 )
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 //左
151 {
152 if (point.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 //右上
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 ///
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 ///
8 /// centrePoint
9 /// fltAngle
10 /// fltRadiusWidth
11 /// 返回值
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 ///
56 /// Resets the size of the title.
57 ///
58 private void ResetTitleSize()
59 {
60 if (!string.IsNullOrEmpty(title))
61 {
62 using (Grap