前提 入行已經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
用處及效果
準備工作
依然用GID+畫的,不懂請自行百度
開始
添加一個類UCThermometer,繼承UserControl
添加一個枚舉,來決定顯示的溫度單位
1 public enum TemperatureUnit 2 { 3 /// <summary> 4 /// 不顯示 5 /// </summary> 6 None, 7 /// <summary> 8 /// 攝氏度 9 /// </summary> 10 C, 11 /// <summary> 12 /// 華氏度 13 /// </summary> 14 F, 15 /// <summary> 16 /// 開氏度 17 /// </summary> 18 K, 19 /// <summary> 20 /// 蘭氏度 21 /// </summary> 22 R, 23 /// <summary> 24 /// 列氏度 25 /// </summary> 26 Re 27 }
添加一些屬性
1 /// <summary> 2 /// The glass tube color 3 /// </summary> 4 private Color glassTubeColor = Color.FromArgb(211, 211, 211); 5 6 /// <summary> 7 /// Gets or sets the color of the glass tube. 8 /// </summary> 9 /// <value>The color of the glass tube.</value> 10 [Description("玻璃管顏色"), Category("自定義")] 11 public Color GlassTubeColor 12 { 13 get { return glassTubeColor; } 14 set 15 { 16 glassTubeColor = value; 17 Refresh(); 18 } 19 } 20 21 /// <summary> 22 /// The mercury color 23 /// </summary> 24 private Color mercuryColor = Color.FromArgb(255, 77, 59); 25 26 /// <summary> 27 /// Gets or sets the color of the mercury. 28 /// </summary> 29 /// <value>The color of the mercury.</value> 30 [Description("水印顏色"), Category("自定義")] 31 public Color MercuryColor 32 { 33 get { return mercuryColor; } 34 set 35 { 36 mercuryColor = value; 37 Refresh(); 38 } 39 } 40 41 /// <summary> 42 /// The minimum value 43 /// </summary> 44 private decimal minValue = 0; 45 /// <summary> 46 /// 左側刻度最小值 47 /// </summary> 48 /// <value>The minimum value.</value> 49 [Description("左側刻度最小值"), Category("自定義")] 50 public decimal MinValue 51 { 52 get { return minValue; } 53 set 54 { 55 minValue = value; 56 Refresh(); 57 } 58 } 59 60 /// <summary> 61 /// The maximum value 62 /// </summary> 63 private decimal maxValue = 100; 64 /// <summary> 65 /// 左側刻度最大值 66 /// </summary> 67 /// <value>The maximum value.</value> 68 [Description("左側刻度最大值"), Category("自定義")] 69 public decimal MaxValue 70 { 71 get { return maxValue; } 72 set 73 { 74 maxValue = value; 75 Refresh(); 76 } 77 } 78 79 /// <summary> 80 /// The m value 81 /// </summary> 82 private decimal m_value = 10; 83 /// <summary> 84 /// 左側刻度值 85 /// </summary> 86 /// <value>The value.</value> 87 [Description("左側刻度值"), Category("自定義")] 88 public decimal Value 89 { 90 get { return m_value; } 91 set 92 { 93 m_value = value; 94 Refresh(); 95 } 96 } 97 98 /// <summary> 99 /// The split count 100 /// </summary> 101 private int splitCount = 0; 102 /// <summary> 103 /// 刻度分隔份數 104 /// </summary> 105 /// <value>The split count.</value> 106 [Description("刻度分隔份數"), Category("自定義")] 107 public int SplitCount 108 { 109 get { return splitCount; } 110 set 111 { 112 if (value <= 0) 113 return; 114 splitCount = value; 115 Refresh(); 116 } 117 } 118 119 /// <summary> 120 /// 獲取或設置控制項顯示的文字的字體。 121 /// </summary> 122 /// <value>The font.</value> 123 /// <PermissionSet> 124 /// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /> 125 /// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /> 126 /// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence" /> 127 /// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /> 128 /// </PermissionSet> 129 [Description("獲取或設置控制項顯示的文字的字體"), Category("自定義")] 130 public override Font Font 131 { 132 get 133 { 134 return base.Font; 135 } 136 set 137 { 138 base.Font = value; 139 Refresh(); 140 } 141 } 142 143 /// <summary> 144 /// 獲取或設置控制項的前景色。 145 /// </summary> 146 /// <value>The color of the fore.</value> 147 /// <PermissionSet> 148 /// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /> 149 /// </PermissionSet> 150 [Description("獲取或設置控制項的文字及刻度顏色"), Category("自定義")] 151 public override System.Drawing.Color ForeColor 152 { 153 get 154 { 155 return base.ForeColor; 156 } 157 set 158 { 159 base.ForeColor = value; 160 Refresh(); 161 } 162 } 163 164 /// <summary> 165 /// The left temperature unit 166 /// </summary> 167 private TemperatureUnit leftTemperatureUnit = TemperatureUnit.C; 168 /// <summary> 169 /// 左側刻度單位,不可為none 170 /// </summary> 171 /// <value>The left temperature unit.</value> 172 [Description("左側刻度單位,不可為none"), Category("自定義")] 173 public TemperatureUnit LeftTemperatureUnit 174 { 175 get { return leftTemperatureUnit; } 176 set 177 { 178 if (value == TemperatureUnit.None) 179 return; 180 leftTemperatureUnit = value; 181 Refresh(); 182 } 183 } 184 185 /// <summary> 186 /// The right temperature unit 187 /// </summary> 188 private TemperatureUnit rightTemperatureUnit = TemperatureUnit.C; 189 /// <summary> 190 /// 右側刻度單位,當為none時,不顯示 191 /// </summary> 192 /// <value>The right temperature unit.</value> 193 [Description("右側刻度單位,當為none時,不顯示"), Category("自定義")] 194 public TemperatureUnit RightTemperatureUnit 195 { 196 get { return rightTemperatureUnit; } 197 set 198 { 199 rightTemperatureUnit = value; 200 Refresh(); 201 } 202 } 203 204 /// <summary> 205 /// The m rect working 206 /// </summary> 207 Rectangle m_rectWorking; 208 /// <summary> 209 /// The m rect left 210 /// </summary> 211 Rectangle m_rectLeft; 212 /// <summary> 213 /// The m rect right 214 /// </summary> 215 Rectangle m_rectRight;
改變大小時,設定畫圖區域
1 void UCThermometer_SizeChanged(object sender, EventArgs e) 2 { 3 m_rectWorking = new Rectangle(this.Width / 2 - this.Width / 8, this.Width / 4, this.Width / 4, this.Height - this.Width / 2); 4 m_rectLeft = new Rectangle(0, m_rectWorking.Top + m_rectWorking.Width / 2, (this.Width - this.Width / 4) / 2 - 2, m_rectWorking.Height - m_rectWorking.Width * 2); 5 m_rectRight = new Rectangle(this.Width - (this.Width - this.Width / 4) / 2 + 2, m_rectWorking.Top + m_rectWorking.Width / 2, (this.Width - this.Width / 4) / 2 - 2, m_rectWorking.Height - m_rectWorking.Width * 2); 6 }
重繪
1 protected override void OnPaint(PaintEventArgs e) 2 { 3 base.OnPaint(e); 4 var g = e.Graphics; 5 g.SetGDIHigh(); 6 7 //玻璃管管 8 GraphicsPath path = new GraphicsPath(); 9 path.AddLine(m_rectWorking.Left, m_rectWorking.Bottom, m_rectWorking.Left, m_rectWorking.Top + m_rectWorking.Width / 2); 10 path.AddArc(new Rectangle(m_rectWorking.Left, m_rectWorking.Top, m_rectWorking.Width, m_rectWorking.Width), 180, 180); 11 path.AddLine(m_rectWorking.Right, m_rectWorking.Top + m_rectWorking.Width / 2, m_rectWorking.Right, m_rectWorking.Bottom); 12 path.CloseAllFigures(); 13 g.FillPath(new SolidBrush(glassTubeColor), path); 14 15 //底部 16 var rectDi = new Rectangle(this.Width / 2 - m_rectWorking.Width, m_rectWorking.Bottom - m_rectWorking.Width - 2, m_rectWorking.Width * 2, m_rectWorking.Width * 2); 17 g.FillEllipse(new SolidBrush(glassTubeColor), rectDi); 18 g.FillEllipse(new SolidBrush(mercuryColor), new Rectangle(rectDi.Left + 4, rectDi.Top + 4, rectDi.Width - 8, rectDi.Height - 8)); 19 20 //刻度 21 decimal decSplit = (maxValue - minValue) / splitCount; 22 decimal decSplitHeight = m_rectLeft.Height / splitCount; 23 for (int i = 0; i <= splitCount; i++) 24 { 25 g.DrawLine(new Pen(new SolidBrush(ForeColor), 1), new PointF(m_rectLeft.Left + 2, (float)(m_rectLeft.Bottom - decSplitHeight * i)), new PointF(m_rectLeft.Right, (float)(m_rectLeft.Bottom - decSplitHeight * i))); 26 27 var valueLeft = (minValue + decSplit * i).ToString("0.##"); 28 var sizeLeft = g.MeasureString(valueLeft, Font); 29 g.DrawString(valueLeft, Font, new SolidBrush(ForeColor), new PointF(m_rectLeft.Left, m_rectLeft.Bottom - (float)decSplitHeight * i - sizeLeft.Height - 1)); 30 31 if (rightTemperatureUnit != TemperatureUnit.None) 32 { 33 g.DrawLine(new Pen(new SolidBrush(Color.Black), 1), new PointF(m_rectRight.Left + 2, (float)(m_rectRight.Bottom - decSplitHeight * i)), new PointF(m_rectRight.Right, (float)(m_rectRight.Bottom - decSplitHeight * i))); 34 var valueRight = GetRightValue(minValue + decSplit * i).ToString("0.##"); 35 var sizeRight = g.MeasureString(valueRight, Font); 36 g.DrawString(valueRight, Font, new SolidBrush(ForeColor), new PointF(m_rectRight.Right - sizeRight.Width - 1, m_rectRight.Bottom - (float)decSplitHeight * i - sizeRight.Height - 1)); 37 } 38 if (i != splitCount) 39 { 40 if (decSplitHeight > 40) 41 { 42 var decSp1 = decSplitHeight / 10; 43 for (int j = 1; j < 10; j++) 44 { 45 if (j == 5) 46 { 47 g.DrawLine(new Pen(new SolidBrush(ForeColor), 1), new PointF(m_rectLeft.Right - 10, (m_rectLeft.Bottom - (float)decSplitHeight * i - ((float)decSp1 * j))), new PointF(m_rectLeft.Right, (m_rectLeft.Bottom - (float)decSplitHeight * i - ((float)decSp1 * j)))); 48 if (rightTemperatureUnit != TemperatureUnit.None) 49 { 50 g.DrawLine(new Pen(new SolidBrush(ForeColor), 1), new PointF(m_rectRight.Left + 10, (m_rectRight.Bottom - (float)decSplitHeight * i - ((float)decSp1 * j))), new PointF(m_rectRight.Left, (m_rectRight.Bottom - (float)decSplitHeight * i - ((float)decSp1 * j)))); 51 } 52 } 53 else 54 { 55 g.DrawLine(new Pen(new SolidBrush(ForeColor), 1), new PointF(m_rectLeft.Right - 5, (m_rectLeft.Bottom - (float)decSplitHeight * i - ((float)decSp1 * j))), new PointF(m_rectLeft.Right, (m_rectLeft.Bottom - (float)decSplitHeight * i - ((float)decSp1 * j)))); 56 if (rightTemperatureUnit != TemperatureUnit.None) 57 { 58 g.DrawLine(new Pen(new SolidBrush(ForeColor), 1), new PointF(m_rectRight.Left + 5, (m_rectRight.Bottom - (float)decSplitHeight * i - ((float)decSp1 * j))), new PointF(m_rectRight.Left, (m_rectRight.Bottom - (float)decSplitHeight * i - ((float)decSp1 * j)))); 59 } 60 } 61 } 62 } 63 else if (decSplitHeight > 10) 64 { 65 g.DrawLine(new Pen(new SolidBrush(ForeColor), 1), new PointF(m_rectLeft.Right - 5, (m_rectLeft.Bottom - (float)decSplitHeight * i - (float)decSplitHeight / 2)), new PointF(m_rectLeft.Right, (m_rectLeft.Bottom - (float)decSplitHeight * i - (float)decSplitHeight / 2))); 66 if (rightTemperatureUnit != TemperatureUnit.None) 67 { 68 g.DrawLine(new Pen(new SolidBrush(ForeColor), 1), new PointF(m_rectRight.Left + 5, (m_rectRight.Bottom - (float)decSplitHeight * i - (float)decSplitHeight / 2)), new PointF(m_rectRight.Left, (m_rectRight.Bottom - (float)decSplitHeight * i - (float)decSplitHeight / 2))); 69 } 70 } 71 } 72 } 73 //單位 74 string strLeftUnit = GetUnitChar(leftTemperatureUnit); 75 g.DrawString(strLeftUnit, Font, new SolidBrush(ForeColor), new PointF(m_rectLeft.Left + 2, 2)); 76 if (rightTemperatureUnit != TemperatureUnit.None) 77 { 78 string strRightUnit = GetUnitChar(rightTemperatureUnit); 79 var rightSize = g.MeasureString(strRightUnit, Font); 80 g.DrawString(strRightUnit, Font, new SolidBrush(ForeColor), new PointF(m_rectRight.Right - 2 - rightSize.Width, 2)); 81 } 82 //值 83 float fltHeightValue = (float)(Value / (maxValue - minValue) * m_rectLeft.Height); 84 RectangleF rectValue = new RectangleF(m_rectWorking.Left + 4, m_rectLeft.Top + (m_rectLeft.Height - fltHeightValue), m_rectWorking.Width - 8, fltHeightValue + (m_rectWorking.Height - m_rectWorking.Width / 2 - m_rectLeft.Height)); 85 g.FillRectangle(new SolidBrush(mercuryColor), rectValue); 86 87 88 var sizeValue = g.MeasureString(m_value.ToString("0.##"), Font); 89 g.DrawString(m_value.ToString("0.##"), Font, new SolidBrush(Color.White), new PointF(rectDi.Left + (rectDi.Width - sizeValue.Width) / 2, rectDi.Top + (rectDi.Height - sizeValue.Height) / 2 + 1)); 90 }
輔助函數
1 private string GetUnitChar(TemperatureUnit unit) 2 { 3 string strUnit = "℃"; 4 switch (unit) 5 { 6 case TemperatureUnit.C: 7 strUnit = "℃"; 8 break; 9 case TemperatureUnit.F: 10 strUnit = "℉"; 11 break; 12 case TemperatureUnit.K: 13 strUnit = "K"; 14 break; 15 case TemperatureUnit.R: 16 strUnit = "°R"; 17 break; 18 case TemperatureUnit.Re: 19 strUnit = "°Re"; 20 break; 21 } 22 return strUnit; 23 } 24 25 private decimal GetRightValue(decimal decValue) 26 { 27 //先將左側的換算為攝氏度 28 var dec = decValue; 29 switch (leftTemperatureUnit) 30 { 31 case TemperatureUnit.F: 32 dec = (decValue - 32) / (9M / 5M); 33 break; 34 case TemperatureUnit.K: 35 dec = decValue - 273;