前提 入行已經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+畫的,不懂的話就百度一下吧
另外主要用到了三角函數,如果不懂,可以向初中的數學老師再問問(你也可以百度一下)
開始
添加一個類UCMeter 繼承 UserControl
首先添加一個需要控制的屬性
1 private int splitCount = 10; 2 /// <summary> 3 /// Gets or sets the split count. 4 /// </summary> 5 /// <value>The split count.</value> 6 [Description("分隔刻度數量,>1"), Category("自定義")] 7 public int SplitCount 8 { 9 get { return splitCount; } 10 set 11 { 12 if (value < 1) 13 return; 14 splitCount = value; 15 Refresh(); 16 } 17 } 18 19 private int meterDegrees = 150; 20 /// <summary> 21 /// Gets or sets the meter degrees. 22 /// </summary> 23 /// <value>The meter degrees.</value> 24 [Description("表盤跨度角度,0-360"), Category("自定義")] 25 public int MeterDegrees 26 { 27 get { return meterDegrees; } 28 set 29 { 30 if (value > 360 || value <= 0) 31 return; 32 meterDegrees = value; 33 Refresh(); 34 } 35 } 36 37 private decimal minValue = 0; 38 /// <summary> 39 /// Gets or sets the minimum value. 40 /// </summary> 41 /// <value>The minimum value.</value> 42 [Description("最小值,<MaxValue"), Category("自定義")] 43 public decimal MinValue 44 { 45 get { return minValue; } 46 set 47 { 48 if (value >= maxValue) 49 return; 50 minValue = value; 51 Refresh(); 52 } 53 } 54 55 private decimal maxValue = 100; 56 /// <summary> 57 /// Gets or sets the maximum value. 58 /// </summary> 59 /// <value>The maximum value.</value> 60 [Description("最大值,>MinValue"), Category("自定義")] 61 public decimal MaxValue 62 { 63 get { return maxValue; } 64 set 65 { 66 if (value <= minValue) 67 return; 68 maxValue = value; 69 Refresh(); 70 } 71 } 72 /// <summary> 73 /// 獲取或設置控制項顯示的文字的字體。 74 /// </summary> 75 /// <value>The font.</value> 76 /// <PermissionSet> 77 /// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /> 78 /// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /> 79 /// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence" /> 80 /// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /> 81 /// </PermissionSet> 82 [Description("刻度字體"), Category("自定義")] 83 public override Font Font 84 { 85 get 86 { 87 return base.Font; 88 } 89 set 90 { 91 base.Font = value; 92 Refresh(); 93 } 94 } 95 96 private decimal m_value = 0; 97 /// <summary> 98 /// Gets or sets the value. 99 /// </summary> 100 /// <value>The value.</value> 101 [Description("值,>=MinValue並且<=MaxValue"), Category("自定義")] 102 public decimal Value 103 { 104 get { return m_value; } 105 set 106 { 107 if (value < minValue || value > maxValue) 108 return; 109 m_value = value; 110 Refresh(); 111 } 112 } 113 114 private MeterTextLocation textLocation = MeterTextLocation.None; 115 /// <summary> 116 /// Gets or sets the text location. 117 /// </summary> 118 /// <value>The text location.</value> 119 [Description("值和固定文字顯示位置"), Category("自定義")] 120 public MeterTextLocation TextLocation 121 { 122 get { return textLocation; } 123 set 124 { 125 textLocation = value; 126 Refresh(); 127 } 128 } 129 130 private string fixedText; 131 /// <summary> 132 /// Gets or sets the fixed text. 133 /// </summary> 134 /// <value>The fixed text.</value> 135 [Description("固定文字"), Category("自定義")] 136 public string FixedText 137 { 138 get { return fixedText; } 139 set 140 { 141 fixedText = value; 142 Refresh(); 143 } 144 } 145 146 private Font textFont = DefaultFont; 147 /// <summary> 148 /// Gets or sets the text font. 149 /// </summary> 150 /// <value>The text font.</value> 151 [Description("值和固定文字字體"), Category("自定義")] 152 public Font TextFont 153 { 154 get { return textFont; } 155 set 156 { 157 textFont = value; 158 Refresh(); 159 } 160 } 161 162 private Color externalRoundColor = Color.FromArgb(255, 77, 59); 163 /// <summary> 164 /// Gets or sets the color of the external round. 165 /// </summary> 166 /// <value>The color of the external round.</value> 167 [Description("外圓顏色"), Category("自定義")] 168 public Color ExternalRoundColor 169 { 170 get { return externalRoundColor; } 171 set 172 { 173 externalRoundColor = value; 174 Refresh(); 175 } 176 } 177 178 private Color insideRoundColor = Color.FromArgb(255, 77, 59); 179 /// <summary> 180 /// Gets or sets the color of the inside round. 181 /// </summary> 182 /// <value>The color of the inside round.</value> 183 [Description("內圓顏色"), Category("自定義")] 184 public Color InsideRoundColor 185 { 186 get { return insideRoundColor; } 187 set 188 { 189 insideRoundColor = value; 190 Refresh(); 191 } 192 } 193 194 private Color boundaryLineColor = Color.FromArgb(255, 77, 59); 195 /// <summary> 196 /// Gets or sets the color of the boundary line. 197 /// </summary> 198 /// <value>The color of the boundary line.</value> 199 [Description("邊界線顏色"), Category("自定義")] 200 public Color BoundaryLineColor 201 { 202 get { return boundaryLineColor; } 203 set 204 { 205 boundaryLineColor = value; 206 Refresh(); 207 } 208 } 209 210 private Color scaleColor = Color.FromArgb(255, 77, 59); 211 /// <summary> 212 /// Gets or sets the color of the scale. 213 /// </summary> 214 /// <value>The color of the scale.</value> 215 [Description("刻度顏色"), Category("自定義")] 216 public Color ScaleColor 217 { 218 get { return scaleColor; } 219 set 220 { 221 scaleColor = value; 222 Refresh(); 223 } 224 } 225 226 private Color scaleValueColor = Color.FromArgb(255, 77, 59); 227 /// <summary> 228 /// Gets or sets the color of the scale value. 229 /// </summary> 230 /// <value>The color of the scale value.</value> 231 [Description("刻度值文字顏色"), Category("自定義")] 232 public Color ScaleValueColor 233 { 234 get { return scaleValueColor; } 235 set 236 { 237 scaleValueColor = value; 238 Refresh(); 239 } 240 } 241 242 private Color pointerColor = Color.FromArgb(255, 77, 59); 243 /// <summary> 244 /// Gets or sets the color of the pointer. 245 /// </summary> 246 /// <value>The color of the pointer.</value> 247 [Description("指針顏色"), Category("自定義")] 248 public Color PointerColor 249 { 250 get { return pointerColor; } 251 set 252 { 253 pointerColor = value; 254 Refresh(); 255 } 256 } 257 258 private Color textColor = Color.FromArgb(255, 77, 59); 259 /// <summary> 260 /// Gets or sets the color of the text. 261 /// </summary> 262 /// <value>The color of the text.</value> 263 [Description("值和固定文字顏色"), Category("自定義")] 264 public Color TextColor 265 { 266 get { return textColor; } 267 set 268 { 269 textColor = value; 270 Refresh(); 271 } 272 } 273 274 Rectangle m_rectWorking;
重繪
1 protected override void OnPaint(PaintEventArgs e) 2 { 3 base.OnPaint(e); 4 var g = e.Graphics; 5 g.SetGDIHigh(); 6 7 //外圓 8 float fltStartAngle = -90 - (meterDegrees) / 2 + 360; 9 var r1 = new Rectangle(m_rectWorking.Location, new Size(m_rectWorking.Width, m_rectWorking.Width)); 10 g.DrawArc(new Pen(new SolidBrush(externalRoundColor), 1), r1, fltStartAngle, meterDegrees); 11 //內圓 12 var r2 = new Rectangle(m_rectWorking.Left + (m_rectWorking.Width - m_rectWorking.Width / 4) / 2, m_rectWorking.Top + (m_rectWorking.Width - m_rectWorking.Width / 4) / 2, m_rectWorking.Width / 4, m_rectWorking.Width / 4); 13 g.DrawArc(new Pen(new SolidBrush(insideRoundColor), 1), r2, fltStartAngle, meterDegrees); 14 15 //邊界線 16 if (meterDegrees != 360) 17 { 18 float fltAngle = fltStartAngle - 180; 19 20 float intY = (float)(m_rectWorking.Top + m_rectWorking.Width / 2 - ((m_rectWorking.Width / 2 - m_rectWorking.Width / 8) * Math.Sin(Math.PI * (fltAngle / 180.00F)))); 21 float intX = (float)(m_rectWorking.Left + (m_rectWorking.Width / 2 - ((m_rectWorking.Width / 2 - m_rectWorking.Width / 8) * Math.Cos(Math.PI * (fltAngle / 180.00F))))); 22 23 float fltY1 = (float)(m_rectWorking.Top + m_rectWorking.Width / 2 - (m_rectWorking.Width / 8 * Math.Sin(Math.PI * (fltAngle / 180.00F)))); 24 float fltX1 = (float)(m_rectWorking.Left + (m_rectWorking.Width / 2 - (m_rectWorking.Width / 8 * Math.Cos(Math.PI * (fltAngle / 180.00F))))); 25 26 g.DrawLine(new Pen(new SolidBrush(boundaryLineColor), 1), new PointF(intX, intY), new PointF(fltX1, fltY1)); 27 g.DrawLine(new Pen(new SolidBrush(boundaryLineColor), 1), new PointF(m_rectWorking.Right - (fltX1 - m_rectWorking.Left), fltY1), new PointF(m_rectWorking.Right - (intX - m_rectWorking.Left), intY)); 28 } 29 30 //分割線 31 int _splitCount = splitCount * 2; 32 float fltSplitValue = (float)meterDegrees / (float)_splitCount; 33 for (int i = 0; i <= _splitCount; i++) 34 { 35 float fltAngle = (fltStartAngle + fltSplitValue * i - 180) % 360; 36 float fltY1 = (float)(m_rectWorking.Top + m_rectWorking.Width / 2 - ((m_rectWorking.Width / 2) * Math.Sin(Math.PI * (fltAngle / 180.00F)))); 37 float fltX1 = (float)(m_rectWorking.Left + (m_rectWorking.Width / 2 - ((m_rectWorking.Width / 2) * Math.Cos(Math.PI * (fltAngle / 180.00F))))); 38 float fltY2 = 0; 39 float fltX2 = 0; 40 if (i % 2 == 0) 41 { 42 fltY2 = (float)(m_rectWorking.Top + m_rectWorking.Width / 2 - ((m_rectWorking.Width / 2 - 10) * Math.Sin(Math.PI * (fltAngle / 180.00F)))); 43 fltX2 = (float)(m_rectWorking.Left + (m_rectWorking.Width / 2 - ((m_rectWorking.Width / 2 - 10) * Math.Cos(Math.PI * (fltAngle / 180.00F))))); 44 if (!(meterDegrees == 360 && i == _splitCount)) 45 { 46 decimal decValue = minValue + (maxValue - minValue) / _splitCount * i; 47 var txtSize = g.MeasureString(decValue.ToString("0.##"), this.Font); 48 float fltFY1 = (float)(m_rectWorking.Top - txtSize.Height / 2 + m_rectWorking.Width / 2 - ((m_rectWorking.Width / 2 - 20) * Math.Sin(Math.PI * (fltAngle / 180.00F)))); 49 float fltFX1 = (float)(m_rectWorking.Left - txtSize.Width / 2 + (m_rectWorking.Width / 2 - ((m_rectWorking.Width / 2 - 20) * Math.Cos(Math.PI * (fltAngle / 180.00F))))); 50 g.DrawString(decValue.ToString("0.##"), Font, new SolidBrush(scaleValueColor), fltFX1, fltFY1); 51 } 52 } 53 else 54 { 55 fltY2 = (float)(m_rectWorking.Top + m_rectWorking.Width / 2 - ((m_rectWorking.Width / 2 - 5) * Math.Sin(Math.PI * (fltAngle / 180.00F)))); 56 fltX2 = (float)(m_rectWorking.Left + (m_rectWorking.Width / 2 - ((m_rectWorking.Width / 2 - 5) * Math.Cos(Math.PI * (fltAngle / 180.00F))))); 57 } 58 g.DrawLine(new Pen(new SolidBrush(scaleColor), i % 2 == 0 ? 2 : 1), new PointF(fltX1, fltY1), new PointF(fltX2, fltY2)); 59 } 60 61 //值文字和固定文字 62 if (textLocation != MeterTextLocation.None) 63 { 64 string str = m_value.ToString("0.##"); 65 var txtSize = g.MeasureString(str, textFont); 66 float fltY = m_rectWorking.Top + m_rectWorking.Width / 4 - txtSize.Height / 2; 67 float fltX = m_rectWorking.Left + m_rectWorking.Width / 2 - txtSize.Width / 2; 68 g.DrawString(str, textFont, new SolidBrush(textColor), new PointF(fltX, fltY)); 69 70 if (!string.IsNullOrEmpty(fixedText)) 71 { 72 str = fixedText; 73 txtSize = g.MeasureString(str, textFont); 74 fltY = m_rectWorking.Top + m_rectWorking.Width / 4 + txtSize.Height / 2; 75 fltX = m_rectWorking.Left + m_rectWorking.Width / 2 - txtSize.Width / 2; 76 g.DrawString(str, textFont, new SolidBrush(textColor), new PointF(fltX, fltY)); 77 } 78 } 79 80 //畫指針 81 g.FillEllipse(new SolidBrush(Color.FromArgb(100, pointerColor.R, pointerColor.G, pointerColor.B)), new Rectangle(m_rectWorking.Left + m_rectWorking.Width / 2 - 10, m_rectWorking.Top + m_rectWorking.Width / 2 - 10, 20, 20)); 82 g.FillEllipse(Brushes.Red, new Rectangle(m_rectWorking.Left + m_rectWorking.Width / 2 - 5, m_rectWorking.Top + m_rectWorking.Width / 2 - 5, 10, 10)); 83 float fltValueAngle = (fltStartAngle + ((float)(m_value - minValue) / (float)(maxValue - minValue)) * (float)meterDegrees - 180) % 360; 84 float intValueY1 = (float)(m_rectWorking.Top + m_rectWorking.Width / 2 - ((m_rectWorking.Width / 2 - 30) * Math.Sin(Math.PI * (fltValueAngle / 180.00F)))); 85 float intValueX1 = (float)(m_rectWorking.Left + (m_rectWorking.Width / 2 - ((m_rectWorking.Width / 2 - 30) * Math.Cos(Math.PI * (fltValueAngle / 180.00F))))); 86 g.DrawLine(new Pen(new SolidBrush(pointerColor), 3), intValueX1, intValueY1, m_rectWorking.Left + m_rectWorking.Width / 2, m_rectWorking.Top + m_rectWorking.Width / 2); 87 }
還有一個顯示文字位置的枚舉
1 /// <summary> 2 /// Enum MeterTextLocation 3 /// </summary> 4 public enum MeterTextLocation 5 { 6 /// <summary> 7 /// The none 8 /// </summary> 9 None,