(四十五)c#Winform自定义控件-水波图表
程序员文章站
2022-04-10 23:31:49
前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。 GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:https://gitee.com/kwwwvagaa/net_winform_custom_contr ......
前提
入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。
github:https://github.com/kwwwvagaa/netwinformcontrol
码云:
如果觉得写的还行,请点个 star 支持一下吧
欢迎前来交流探讨: 企鹅群568015492
麻烦博客下方点个【推荐】,谢谢
nuget
install-package hzh_controls
目录
用处及效果
准备工作
这个用到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 }
最后的话
如果你喜欢的话,请到 点个星星吧
上一篇: .NET Core 很酷,你不得不知!