前提 入行已經7,8年了,一直想做一套漂亮點的自定義控制項,於是就有了本系列文章。 GitHub:https://github.com/kwwwvagaa/NetWinformControl 碼雲:https://gitee.com/kwwwvagaa/net_winform_custom_contr ...
前提
入行已經7,8年了,一直想做一套漂亮點的自定義控制項,於是就有了本系列文章。
GitHub:https://github.com/kwwwvagaa/NetWinformControl
碼雲:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
如果覺得寫的還行,請點個 star 支持一下吧
麻煩博客下方點個【推薦】,謝謝
NuGet
Install-Package HZH_Controls
目錄
https://www.cnblogs.com/bfyx/p/11364884.html
用處及效果
準備工作
GDI+畫的,不會的可以先百度瞭解下
開始
添加一個類UCRadarChart ,繼承 UserControl
添加一些控制屬性
1 /// <summary> 2 /// The split count 3 /// </summary> 4 private int splitCount = 5; 5 /// <summary> 6 /// Gets or sets the split count. 7 /// </summary> 8 /// <value>The split count.</value> 9 [Browsable(true)] 10 [Category("自定義")] 11 [Description("獲取或設置分隔份數")] 12 public int SplitCount 13 { 14 get { return splitCount; } 15 set 16 { 17 splitCount = value; 18 Invalidate(); 19 } 20 } 21 22 /// <summary> 23 /// The split odd color 24 /// </summary> 25 private Color splitOddColor = Color.White; 26 /// <summary> 27 /// 分隔奇數欄背景色 28 /// </summary> 29 /// <value>The color of the split odd.</value> 30 [Browsable(true)] 31 [Category("自定義")] 32 [Description("獲取或設置分隔奇數欄背景色")] 33 public Color SplitOddColor 34 { 35 get { return splitOddColor; } 36 set 37 { 38 splitOddColor = value; 39 Invalidate(); 40 } 41 } 42 /// <summary> 43 /// The split even color 44 /// </summary> 45 private Color splitEvenColor = Color.FromArgb(232, 232, 232); 46 /// <summary> 47 /// 分隔偶數欄背景色 48 /// </summary> 49 /// <value>The color of the split even.</value> 50 [Browsable(true)] 51 [Category("自定義")] 52 [Description("獲取或設置分隔偶數欄背景色")] 53 public Color SplitEvenColor 54 { 55 get { return splitEvenColor; } 56 set { splitEvenColor = value; } 57 } 58 59 /// <summary> 60 /// The line color 61 /// </summary> 62 private Color lineColor = Color.FromArgb(153, 153, 153); 63 /// <summary> 64 /// Gets or sets the color of the line. 65 /// </summary> 66 /// <value>The color of the line.</value> 67 [Browsable(true)] 68 [Category("自定義")] 69 [Description("獲取或設置線條色")] 70 public Color LineColor 71 { 72 get { return lineColor; } 73 set 74 { 75 lineColor = value; 76 Invalidate(); 77 } 78 } 79 80 /// <summary> 81 /// The radar positions 82 /// </summary> 83 private RadarPosition[] radarPositions; 84 /// <summary> 85 /// 節點列表,至少需要3個 86 /// </summary> 87 /// <value>The radar positions.</value> 88 [Browsable(true)] 89 [Category("自定義")] 90 [Description("獲取或設置節點,至少需要3個")] 91 public RadarPosition[] RadarPositions 92 { 93 get { return radarPositions; } 94 set 95 { 96 radarPositions = value; 97 Invalidate(); 98 } 99 } 100 101 /// <summary> 102 /// The title 103 /// </summary> 104 private string title; 105 /// <summary> 106 /// 標題 107 /// </summary> 108 /// <value>The title.</value> 109 [Browsable(true)] 110 [Category("自定義")] 111 [Description("獲取或設置標題")] 112 public string Title 113 { 114 get { return title; } 115 set 116 { 117 title = value; 118 ResetTitleSize(); 119 Invalidate(); 120 } 121 } 122 123 /// <summary> 124 /// The title font 125 /// </summary> 126 private Font titleFont = new Font("微軟雅黑", 12); 127 /// <summary> 128 /// Gets or sets the title font. 129 /// </summary> 130 /// <value>The title font.</value> 131 [Browsable(true)] 132 [Category("自定義")] 133 [Description("獲取或設置標題字體")] 134 public Font TitleFont 135 { 136 get { return titleFont; } 137 set 138 { 139 titleFont = value; 140 ResetTitleSize(); 141 Invalidate(); 142 } 143 } 144 145 /// <summary> 146 /// The title color 147 /// </summary> 148 private Color titleColor = Color.Black; 149 /// <summary> 150 /// Gets or sets the color of the title. 151 /// </summary> 152 /// <value>The color of the title.</value> 153 [Browsable(true)] 154 [Category("自定義")] 155 [Description("獲取或設置標題文本顏色")] 156 public Color TitleColor 157 { 158 get { return titleColor; } 159 set 160 { 161 titleColor = value; 162 Invalidate(); 163 } 164 } 165 166 /// <summary> 167 /// The lines 168 /// </summary> 169 private RadarLine[] lines; 170 /// <summary> 171 /// Gets or sets the lines. 172 /// </summary> 173 /// <value>The lines.</value> 174 [Browsable(true)] 175 [Category("自定義")] 176 [Description("獲取或設置值線條,Values長度必須與RadarPositions長度一致,否則無法顯示")] 177 public RadarLine[] Lines 178 { 179 get { return lines; } 180 set 181 { 182 lines = value; 183 Invalidate(); 184 } 185 } 186 187 188 /// <summary> 189 /// The title size 190 /// </summary> 191 SizeF titleSize = SizeF.Empty; 192 /// <summary> 193 /// The m rect working 194 /// </summary> 195 private RectangleF m_rectWorking = Rectangle.Empty; 196 /// <summary> 197 /// The line value type size 198 /// </summary> 199 SizeF lineValueTypeSize = SizeF.Empty; 200 /// <summary> 201 /// The int line value COM count 202 /// </summary> 203 int intLineValueComCount = 0; 204 /// <summary> 205 /// The int line value row count 206 /// </summary> 207 int intLineValueRowCount = 0;
屬性改變時處理工作區域
1 /// <summary> 2 /// Handles the SizeChanged event of the UCRadarChart control. 3 /// </summary> 4 /// <param name="sender">The source of the event.</param> 5 /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> 6 void UCRadarChart_SizeChanged(object sender, EventArgs e) 7 { 8 ResetWorkingRect(); 9 } 10 11 /// <summary> 12 /// Resets the working rect. 13 /// </summary> 14 private void ResetWorkingRect() 15 { 16 if (lines != null && lines.Length > 0) 17 { 18 using (Graphics g = this.CreateGraphics()) 19 { 20 foreach (var item in lines) 21 { 22 var s = g.MeasureString(item.Name, Font); 23 if (s.Width > lineValueTypeSize.Width) 24 lineValueTypeSize = s; 25 } 26 } 27 } 28 var lineTypePanelHeight = 0f; 29 if (lineValueTypeSize != SizeF.Empty) 30 { 31 intLineValueComCount = (int)(this.Width / (lineValueTypeSize.Width + 25)); 32 33 intLineValueRowCount = lines.Length / intLineValueComCount; 34 if (lines.Length % intLineValueComCount != 0) 35 { 36 intLineValueRowCount++; 37 } 38 lineTypePanelHeight = (lineValueTypeSize.Height + 10) * intLineValueRowCount; 39 } 40 var min = Math.Min(this.Width, this.Height - titleSize.Height - lineTypePanelHeight); 41 var rectWorking = new RectangleF((this.Width - min) / 2 + 10, titleSize.Height + lineTypePanelHeight + 10, min - 10, min - 10); 42 //處理文字 43 float fltSplitAngle = 360F / radarPositions.Length; 44 float fltRadiusWidth = rectWorking.Width / 2; 45 float minX = rectWorking.Left; 46 float maxX = rectWorking.Right; 47 float minY = rectWorking.Top; 48 float maxY = rectWorking.Bottom; 49 using (Graphics g = this.CreateGraphics()) 50 { 51 PointF centrePoint = new PointF(rectWorking.Left + rectWorking.Width / 2, rectWorking.Top + rectWorking.Height / 2); 52 for (int i = 0; i < radarPositions.Length; i++) 53 { 54 float fltAngle = 270 + fltSplitAngle * i; 55 fltAngle = fltAngle % 360; 56 PointF _point = GetPointByAngle(centrePoint, fltAngle, fltRadiusWidth); 57 var _txtSize = g.MeasureString(radarPositions[i].Text, Font); 58 if (_point.X < centrePoint.X)//左 59 { 60 if (_point.X - _txtSize.Width < minX) 61 { 62 minX = rectWorking.Left + _txtSize.Width; 63 } 64 } 65 else//右 66 { 67 if (_point.X + _txtSize.Width > maxX) 68 { 69 maxX = rectWorking.Right - _txtSize.Width; 70 } 71 } 72 if (_point.Y < centrePoint.Y)//上 73 { 74 if (_point.Y - _txtSize.Height < minY) 75 { 76 minY = rectWorking.Top + _txtSize.Height; 77 } 78 } 79 else//下 80 { 81 if (_point.Y + _txtSize.Height > maxY) 82 { 83 maxY = rectWorking.Bottom - _txtSize.Height; 84 } 85 } 86 } 87 } 88 89 min = Math.Min(maxX - minX, maxY - minY); 90 m_rectWorking = new RectangleF(minX, minY, min, min); 91 }
重繪
1 protected override void OnPaint(PaintEventArgs e) 2 { 3 base.OnPaint(e); 4 var g = e.Graphics; 5 g.SetGDIHigh(); 6 7 if (!string.IsNullOrEmpty(title)) 8 { 9 g.DrawString(title, titleFont, new SolidBrush(titleColor), new RectangleF(m_rectWorking.Left + (m_rectWorking.Width - titleSize.Width) / 2, m_rectWorking.Top - titleSize.Height - 10 - (intLineValueRowCount * (10 + lineValueTypeSize.Height)), titleSize.Width, titleSize.Height)); 10 } 11 12 if (radarPositions.Length <= 2) 13 { 14 g.DrawString("至少需要3個頂點", Font, new SolidBrush(Color.Black), m_rectWorking, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }); 15 return; 16 } 17 18 var y = m_rectWorking.Top - 20 - (intLineValueRowCount * (10 + lineValueTypeSize.Height)); 19 20 for (int i = 0; i < intLineValueRowCount; i++) 21 { 22 var x = 0f; 23 int intCount = intLineValueComCount; 24 if (i == intLineValueRowCount - 1) 25 { 26 intCount = lines.Length % intLineValueComCount; 27 28 } 29 x = m_rectWorking.Left + (m_rectWorking.Width - intCount * (lineValueTypeSize.Width + 25)) / 2; 30 31 for (int j = 0; j < intCount; j++) 32 { 33 g.FillRectangle(new SolidBrush(lines[i * intLineValueComCount + j].LineColor.Value), new RectangleF(x + (lineValueTypeSize.Width + 25)*j, y + lineValueTypeSize.Height * i, 15, lineValueTypeSize.Height)); 34 g.DrawString(lines[i * intLineValueComCount + j].Name, Font, new SolidBrush(lines[i * intLineValueComCount + j].LineColor.Value), new PointF(x + (lineValueTypeSize.Width + 25) * j + 20, y + lineValueTypeSize.Height * i)); 35 } 36 } 37 38 float fltSplitAngle = 360F / radarPositions.Length; 39 float fltRadiusWidth = m_rectWorking.Width / 2; 40 float fltSplitRadiusWidth = fltRadiusWidth / splitCount; 41 PointF centrePoint = new PointF(m_rectWorking.Left + m_rectWorking.Width / 2, m_rectWorking.Top + m_rectWorking.Height / 2); 42 43 List<List<PointF>> lstRingPoints = new List<List<PointF>>(splitCount); 44 //分割點 45 for (int i = 0; i < radarPositions.Length; i++) 46 { 47 float fltAngle = 270 + fltSplitAngle * i; 48 fltAngle = fltAngle % 360; 49 for (int j = 0; j < splitCount; j++) 50 { 51 if (i == 0) 52 { 53 lstRingPoints.Add(new List<PointF>()); 54 } 55 PointF _point = GetPointByAngle(centrePoint, fltAngle, fltSplitRadiusWidth * (splitCount - j)); 56 lstRingPoints[j].Add(_point); 57 } 58 } 59 60 for (int i = 0; i < lstRingPoints.Count; i++) 61 { 62 var ring = lstRingPoints[i]; 63 GraphicsPath path = new GraphicsPath(); 64 path.AddLines(ring.ToArray()); 65 if ((lstRingPoints.Count - i) % 2 == 0) 66 { 67 g.FillPath(new SolidBrush(splitEvenColor), path); 68 } 69 else 70 { 71 g.FillPath(new SolidBrush(splitOddColor), path); 72 } 73 } 74 75 //畫環 76 foreach (var ring in lstRingPoints) 77 { 78 ring.Add(ring[0]); 79 g.DrawLines(new Pen(new SolidBrush(lineColor)), ring.ToArray()); 80 } 81 //分割線 82 foreach (var item in lstRingPoints[0]) 83 { 84 g.DrawLine(new Pen(new SolidBrush(lineColor)), centrePoint, item); 85 } 86 87 //值 88 for (int i = 0; i < lines.Length; i++) 89 { 90 var line = lines[i]; 91 if (line.Values.Length != radarPositions.Length)//如果數據長度和節點長度不一致則不繪製 92 continue; 93 if (line.LineColor == null || line.LineColor == Color.Empty || line.LineColor == Color.Transparent) 94 line.LineColor = ControlHelper.Colors[i + 13]; 95 List<PointF> ps = new List<PointF>(); 96 for (int j = 0; j < radarPositions.Length; j++) 97 { 98 float fltAngle = 270 + fltSplitAngle * j; 99 fltAngle = fltAngle % 360; 100 PointF _point = GetPointByAngle(centrePoint, fltAngle, fltRadiusWidth * (float)(line.Values[j] / radarPositions[i].MaxValue)); 101 ps.Add(_point); 102 } 103 ps.Add(ps[0]); 104 if (line.FillColor != null && line.FillColor != Color.Empty && line.FillColor != Color.Transparent) 105 { 106 GraphicsPath path = new GraphicsPath(); 107 path.AddLines(ps.ToArray()); 108 g.FillPath(new SolidBrush(line.FillColor.Value), path); 109 } 110 g.DrawLines(new Pen(new SolidBrush(line.LineColor.Value), 2), ps.ToArray()); 111 112 for (int j = 0; j < radarPositions.Length; j++) 113 { 114 var item = ps[j]; 115 g.FillEllipse(new SolidBrush(Color.White), new RectangleF(item.X - 3, item.Y - 3, 6, 6)); 116 g.DrawEllipse(new Pen(new SolidBrush(line.LineColor.Value)), new RectangleF(item.X - 3, item.Y - 3, 6, 6)); 117 if (line.ShowValueText) 118 { 119 var valueSize = g.MeasureString(line.Values[j].ToString("0.##"), Font);