前提
入行已经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+画的,请先了解一下GDI+
还有用到了基类控件UCControlBase来控制圆角和背景色,如果还不了解请移步查看
开始
添加一个类UCWaveWithSource ,继承UCControlBase
添加属性
private int m_waveActualWidth = 50; private int m_waveWidth = 50; [Description("波形宽度"), Category("自定义")] public int WaveWidth { get { return m_waveWidth; } set { if (value <= 0) return; m_waveWidth = value; ResetWaveCount(); Refresh(); } } private int m_sleepTime = 1000; /// <summary> /// 波运行速度(运行时间间隔,毫秒) /// </summary> [Description("运行速度(运行时间间隔,毫秒)"), Category("自定义")] public int SleepTime { get { return m_sleepTime; } set { if (value <= 0) return; m_sleepTime = value; if (timer != null) { timer.Enabled = false; timer.Interval = value; timer.Enabled = true; } } } private float m_lineTension = 0.5f; /// <summary> /// 线弯曲程度 /// </summary> [Description("线弯曲程度(0-1)"), Category("自定义")] public float LineTension { get { return m_lineTension; } set { if (!(value >= 0 && value <= 1)) { return; } m_lineTension = value; Refresh(); } } private Color m_lineColor = Color.FromArgb(150, 73, 119, 232); [Description("曲线颜色"), Category("自定义")] public Color LineColor { get { return m_lineColor; } set { m_lineColor = value; Refresh(); } } private Color m_gridLineColor = Color.FromArgb(50, 73, 119, 232); [Description("网格线颜色"), Category("自定义")] public Color GridLineColor { get { return m_gridLineColor; } set { m_gridLineColor = value; Refresh(); } } private Color m_gridLineTextColor = Color.FromArgb(150, 73, 119, 232); [Description("网格文本颜色"), Category("自定义")] public Color GridLineTextColor { get { return m_gridLineTextColor; } set { m_gridLineTextColor = value; Refresh(); } } public override Font Font { get { return base.Font; } set { base.Font = value; } } /// <summary> /// 数据源,用以缓存所有需要显示的数据 /// </summary> List<KeyValuePair<string, double>> m_dataSource = new List<KeyValuePair<string, double>>(); /// <summary> /// 当前需要显示的数据 /// </summary> List<KeyValuePair<string, double>> m_currentSource = new List<KeyValuePair<string, double>>(); Timer timer = new Timer(); /// <summary> /// 画图区域 /// </summary> Rectangle m_drawRect; int m_waveCount = 0;
构造函数中初始化一下样式
1 public UCWaveWithSource() 2 { 3 this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); 4 this.SetStyle(ControlStyles.DoubleBuffer, true); 5 this.SetStyle(ControlStyles.ResizeRedraw, true); 6 this.SetStyle(ControlStyles.Selectable, true); 7 this.SetStyle(ControlStyles.SupportsTransparentBackColor, true); 8 this.SetStyle(ControlStyles.UserPaint, true); 9 10 this.SizeChanged += UCWaveWithSource_SizeChanged; 11 this.IsShowRect = true; 12 this.RectColor = Color.FromArgb(232, 232, 232); 13 this.FillColor = Color.FromArgb(197, 229, 250); 14 this.RectWidth = 1; 15 this.ConerRadius = 10; 16 this.IsRadius = true; 17 this.Size = new Size(300, 200); 18 19 timer.Interval = m_sleepTime; 20 timer.Tick += timer_Tick; 21 this.VisibleChanged += UCWave_VisibleChanged; 22 }
一个数据添加的函数
1 /// <summary> 2 /// 添加需要显示的数据 3 /// </summary> 4 /// <param name="key">名称</param> 5 /// <param name="value">值</param> 6 public void AddSource(string key, double value) 7 { 8 m_dataSource.Add(new KeyValuePair<string, double>(key, value)); 9 }
重绘
1 protected override void OnPaint(PaintEventArgs e) 2 { 3 base.OnPaint(e); 4 var g = e.Graphics; 5 g.SetGDIHigh(); 6 7 int intLineSplit = m_drawRect.Height / 4; 8 for (int i = 0; i <= 4; i++) 9 { 10 var pen = new Pen(new SolidBrush(m_gridLineColor), 1); 11 // pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot; 12 g.DrawLine(pen, m_drawRect.Left, m_drawRect.Bottom - 1 - i * intLineSplit, m_drawRect.Right, m_drawRect.Bottom - 1 - i * intLineSplit); 13 } 14 15 if (m_currentSource == null || m_currentSource.Count <= 0) 16 { 17 for (int i = 0; i <= 4; i++) 18 { 19 string strText = (100 / 4 * i).ToString(); 20 System.Drawing.SizeF _numSize = g.MeasureString(strText, this.Font); 21 g.DrawString(strText, Font, new SolidBrush(m_gridLineTextColor), m_drawRect.Left - _numSize.Width - 1, m_drawRect.Bottom - 1 - i * intLineSplit - (_numSize.Height / 2)); 22 } 23 return; 24 } 25 List<Point> lst1 = new List<Point>(); 26 double dblValue = m_currentSource.Max(p => p.Value); 27 int intValue = (int)dblValue; 28 int intDivisor = ("1".PadRight(intValue.ToString().Length - 1, '0')).ToInt(); 29 if (intDivisor < 100) 30 intDivisor = 100; 31 int intTop = intValue; 32 if (intValue % intDivisor != 0) 33 { 34 intTop = (intValue / intDivisor + 1) * intDivisor; 35 } 36 if (intTop == 0) 37 intTop = 100; 38 39 for (int i = 0; i <= 4; i++) 40 { 41 string strText = (intTop / 4 * i).ToString(); 42 System.Drawing.SizeF _numSize = g.MeasureString(strText, this.Font); 43 g.DrawString(strText, Font, new SolidBrush(m_gridLineTextColor), m_drawRect.Left - _numSize.Width - 1, m_drawRect.Bottom - 1 - i * intLineSplit - (_numSize.Height / 2)); 44 } 45 46 int intEndX = 0; 47 int intEndY = 0; 48 for (int i = 0; i < m_currentSource.Count; i++) 49 { 50 intEndX = i * m_waveActualWidth + m_drawRect.X; 51 intEndY = m_drawRect.Bottom - 1 - (int)(m_currentSource[i].Value / intTop * m_drawRect.Height); 52 lst1.Add(new Point(intEndX, intEndY)); 53 if (!string.IsNullOrEmpty(m_currentSource[i].Key)) 54 { 55 System.Drawing.SizeF _numSize = g.MeasureString(m_currentSource[i].Key, this.Font); 56 int txtX = intEndX - (int)(_numSize.Width / 2) + 1; 57 g.DrawString(m_currentSource[i].Key, Font, new SolidBrush(m_gridLineTextColor), new PointF(txtX, m_drawRect.Bottom + 5)); 58 } 59 } 60 61 int intFirstY = m_drawRect.Bottom - 1 - (int)(m_currentSource[0].Value / intTop * m_drawRect.Height); 62 63 64 GraphicsPath path1 = new GraphicsPath(); 65 path1.AddCurve(lst1.ToArray(), m_lineTension); 66 g.DrawPath(new Pen(new SolidBrush(m_lineColor), 1), path1); 67 68 }
辅助函数
1 /// <summary> 2 /// 得到当前需要画图的数据 3 /// </summary> 4 /// <returns></returns> 5 private List<KeyValuePair<string, double>> GetCurrentList() 6 { 7 if (m_dataSource.Count < m_waveCount) 8 { 9 int intCount = m_waveCount - m_dataSource.Count; 10 for (int i = 0; i < intCount; i++) 11 { 12 m_dataSource.Add(new KeyValuePair<string, double>("", 0)); 13 } 14 } 15 16 var lst = m_dataSource.GetRange(0, m_waveCount); 17 if (lst.Count == 1) 18 lst.Insert(0, new KeyValuePair<string, double>("", 0)); 19 return lst; 20 } 21 22 /// <summary> 23 /// 计算需要显示的个数 24 /// </summary> 25 private void ResetWaveCount() 26 { 27 m_waveCount = m_drawRect.Width / m_waveWidth; 28 m_waveActualWidth = m_waveWidth + (m_drawRect.Width % m_waveWidth) / m_waveCount; 29 m_waveCount++; 30 if (m_dataSource.Count < m_waveCount) 31 { 32 int intCount = m_waveCount - m_dataSource.Count; 33 for (int i = 0; i < intCount; i++) 34 { 35 m_dataSource.Insert(0, new KeyValuePair<string, double>("", 0)); 36 } 37 } 38 }
完整代码
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Drawing; 5 using System.Drawing.Drawing2D; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace HZH_Controls.Controls 11 { 12 public class UCWaveWithSource : UCControlBase 13 { 14 private int m_waveActualWidth = 50; 15 16 private int m_waveWidth = 50; 17 18 [Description("波形宽度"), Category("自定义")] 19 public int WaveWidth 20 { 21 get { return m_waveWidth; } 22 set 23 { 24 if (value <= 0) 25 return; 26 m_waveWidth = value; 27 ResetWaveCount(); 28 Refresh(); 29 } 30 } 31 32 private int m_sleepTime = 1000; 33 /// <summary> 34 /// 波运行速度(运行时间间隔,毫秒) 35 /// </summary> 36 [Description("运行速度(运行时间间隔,毫秒)"), Category("自定义")] 37 public int SleepTime 38 { 39 get { return m_sleepTime; } 40 set 41 { 42 if (value <= 0) 43 return; 44 m_sleepTime = value; 45 if (timer != null) 46 { 47 timer.Enabled = false; 48 timer.Interval = value; 49 timer.Enabled = true; 50 } 51 } 52 } 53 54 private float m_lineTension = 0.5f; 55 /// <summary> 56 /// 线弯曲程度 57 /// </summary> 58 [Description("线弯曲程度(0-1)"), Category("自定义")] 59 public float LineTension 60 { 61 get { return m_lineTension; } 62 set 63 { 64 if (!(value >= 0 && value <= 1)) 65 { 66 return; 67 } 68 m_lineTension = value; 69 Refresh(); 70 } 71 } 72 73 private Color m_lineColor = Color.FromArgb(150, 73, 119, 232); 74 75 [Description("曲线颜色"), Category("自定义")] 76 public Color LineColor 77 { 78 get { return m_lineColor; } 79 set 80 { 81 m_lineColor = value; 82 Refresh(); 83 84 } 85 } 86 87 private Color m_gridLineColor = Color.FromArgb(50, 73, 119, 232); 88 89 [Description("网格线颜色"), Category("自定义")] 90 public Color GridLineColor 91 { 92 get { return m_gridLineColor; } 93 set 94 { 95 m_gridLineColor = value; 96 Refresh(); 97 } 98 } 99 100 private Color m_gridLineTextColor = Color.FromArgb(150, 73, 119, 232); 101 102 [Description("网格文本颜色"), Category("自定义")] 103 public Color GridLineTextColor 104 { 105 get { return m_gridLineTextColor; } 106 set 107 { 108 m_gridLineTextColor = value; 109 Refresh(); 110 } 111 } 112 113 public override Font Font 114 { 115 get 116 { 117 return base.Font; 118 } 119 set 120 { 121 base.Font = value; 122 } 123 } 124 /// <summary> 125 /// 数据源,用以缓存所有需要显示的数据 126 /// </summary> 127 List<KeyValuePair<string, double>> m_dataSource = new List<KeyValuePair<string, double>>(); 128 /// <summary> 129 /// 当前需要显示的数据 130 /// </summary> 131 List<KeyValuePair<string, double>> m_currentSource = new List<KeyValuePair<string, double>>(); 132 Timer timer = new Timer(); 133 /// <summary> 134 /// 画图区域 135 /// </summary> 136 Rectangle m_drawRect; 137 138 int m_waveCount = 0; 139 public UCWaveWithSource() 140 { 141 this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); 142 this.SetStyle(ControlStyles.DoubleBuffer, true); 143 this.SetStyle(ControlStyles.ResizeRedraw, true); 144 this.SetStyle(ControlStyles.Selectable, true); 145 this.SetStyle(ControlStyles.SupportsTransparentBackColor, true); 146 this.SetStyle(ControlStyles.UserPaint, true); 147 148 this.SizeChanged += UCWaveWithSource_SizeChanged; 149 this.IsShowRect = true; 150 this.RectColor = Color.FromArgb(232, 232, 232); 151 this.FillColor = Color.FromArgb(197, 229, 250); 152 this.RectWidth = 1; 153 this.ConerRadius = 10; 154 this.IsRadius = true; 155 this.Size = new Size(300, 200); 156 157 timer.Interval = m_sleepTime; 158 timer.Tick += timer_Tick; 159 this.VisibleChanged += UCWave_VisibleChanged; 160 } 161 162 163 /// <summary> 164 /// 添加需要显示的数据 165 /// </summary> 166 /// <param name="key">名称</param> 167 /// <param name="value">值</param> 168 public void AddSource(string key, double value) 169 { 170 m_dataSource.Add(new KeyValuePair<string, double>(key, value)); 171 } 172 173 void UCWave_VisibleChanged(object sender, EventArgs e) 174 { 175 if (!DesignMode) 176 { 177 timer.Enabled = this.Visible; 178 } 179 } 180 181 void timer_Tick(object sender, EventArgs e) 182 { 183 m_currentSource = GetCurrentList(); 184 m_dataSource.RemoveAt(0); 185 this.Refresh(); 186 } 187 void UCWaveWithSource_SizeChanged(object sender, EventArgs e) 188 { 189 m_drawRect = new Rectangle(60, 20, this.Width - 80, this.Height - 60); 190 ResetWaveCount(); 191 } 192 193 protected override void OnPaint(PaintEventArgs e) 194 { 195 base.OnPaint(e); 196 var g = e.Graphics; 197 g.SetGDIHigh(); 198 199 int intLineSplit = m_drawRect.Height / 4; 200 for (int i = 0; i <= 4; i++) 201 { 202 var pen = new Pen(new SolidBrush(m_gridLineColor), 1); 203 // pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot; 204 g.DrawLine(pen, m_drawRect.Left, m_drawRect.Bottom - 1 - i * intLineSplit, m_drawRect.Right, m_drawRect.Bottom - 1 - i * intLineSplit); 205 } 206 207 if (m_currentSource == null || m_currentSource.Count <= 0) 208 { 209 for (int i = 0; i <= 4; i++) 210 { 211 string strText = (100 / 4 * i).ToString(); 212 System.Drawing.SizeF _numSize = g.MeasureString(strText, this.Font); 213 g.DrawString(strText, Font, new SolidBrush(m_gridLineTextColor), m_drawRect.Left - _numSize.Width - 1, m_drawRect.Bottom - 1 - i * intLineSplit - (_numSize.Height / 2)); 214 } 215 return; 216 } 217 List<Point> lst1 = new List<Point>(); 218 double dblValue = m_currentSource.Max(p => p.Value); 219 int intValue = (int)dblValue; 220 int intDivisor = ("1".PadRight(intValue.ToString().Length - 1, '0')).ToInt(); 221 if (intDivisor < 100) 222 intDivisor = 100; 223 int intTop = intValue; 224 if (intValue % intDivisor != 0) 225 { 226 intTop = (intValue / intDivisor + 1) * intDivisor; 227 } 228 if (intTop == 0) 229 intTop = 100; 230 231 for (int i = 0; i <= 4; i++) 232 { 233 string strText = (intTop / 4 * i).ToString(); 234 System.Drawing.SizeF _numSize = g.MeasureString(strText, this.Font); 235 g.DrawString(strText, Font, new SolidBrush(m_gridLineTextColor), m_drawRect.Left - _numSize.Width - 1, m_drawRect.Bottom - 1 - i * intLineSplit - (_numSize.Height / 2)); 236 } 237 238 int intEndX = 0; 239 int intEndY = 0; 240 for (int i = 0; i < m_currentSource.Count; i++) 241 { 242 intEndX = i * m_waveActualWidth + m_drawRect.X; 243 intEndY = m_drawRect.Bottom - 1 - (int)(m_currentSource[i].Value / intTop * m_drawRect.Height); 244 lst1.Add(new Point(intEndX, intEndY)); 245 if (!string.IsNullOrEmpty(m_currentSource[i].Key)) 246 { 247 System.Drawing.SizeF _numSize = g.MeasureString(m_currentSource[i].Key, this.Font); 248 int txtX = intEndX - (int)(_numSize.Width / 2) + 1; 249 g.DrawString(m_currentSource[i].Key, Font, new SolidBrush(m_gridLineTextColor), new PointF(txtX, m_drawRect.Bottom + 5)); 250 } 251 } 252 253 int intFirstY = m_drawRect.Bottom - 1 - (int)(m_currentSource[0].Value / intTop * m_drawRect.Height); 254 255 256 GraphicsPath path1 = new GraphicsPath(); 257 path1.AddCurve(lst1.ToArray(), m_lineTension); 258 g.DrawPath(new Pen(new SolidBrush(m_lineColor), 1), path1); 259 260 } 261 /// <summary> 262 /// 得到当前需要画图的数据 263 /// </summary> 264 /// <returns></returns> 265 private List<KeyValuePair<string, double>> GetCurrentList() 266 { 267 if (m_dataSource.Count < m_waveCount) 268 { 269 int intCount = m_waveCount - m_dataSource.Count; 270 for (int i = 0; i < intCount; i++) 271 { 272 m_dataSource.Add(new KeyValuePair<string, double>("", 0)); 273 } 274 } 275 276 var lst = m_dataSource.GetRange(0, m_waveCount); 277 if (lst.Count == 1) 278 lst.Insert(0, new KeyValuePair<string, double>("", 0)); 279 return lst; 280 } 281 282 /// <summary> 283 /// 计算需要显示的个数 284 /// </summary> 285 private void ResetWaveCount() 286 { 287 m_waveCount = m_drawRect.Width / m_waveWidth; 288 m_waveActualWidth = m_waveWidth + (m_drawRect.Width % m_waveWidth) / m_waveCount; 289 m_waveCount++; 290 if (m_dataSource.Count < m_waveCount) 291 { 292 int intCount = m_waveCount - m_dataSource.Count; 293 for (int i = 0; i < intCount; i++) 294 { 295 m_dataSource.Insert(0, new KeyValuePair<string, double>("", 0)); 296 } 297 } 298 } 299 } 300 }
最后的话
如果你喜欢的话,请到 https://gitee.com/kwwwvagaa/net_winform_custom_control 点个星星吧