入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。
GitHub:https://github.com/kwwwvagaa/NetWinformControl
码云:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
如果觉得写的还行,请点个 star 支持一下吧
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 点个星星吧