博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Nobi's StatusChart 野比的状态波形图控件
阅读量:2456 次
发布时间:2019-05-10

本文共 2393 字,大约阅读时间需要 7 分钟。

老牌开源波形图控件,尽管有些简陋,几年来仍被大量转载。超过数千人从项目中有所收获。

野比这几年忙于个人事务和回答网友疑问,一直未能抽出时间维护,深感遗憾。

Nobi's StatusChart - 野比的状态波形图控件

作者:野比 ()

封面图片为野比原创,请勿未经允许私自引用

从构思到实现

野比 著

背景

目前比较流行的 WinForm 程序设计都会提供形象的可视化数据流动记录功能,

如 FlashGet 及其衍生软件的悬浮窗网速监视图,Windows 任务管理器的 CPU、内存使用图等。

 

  

 

构思

为了在我们自己的程序中实现这种效果,就需要研究、分析它们的原理,掌握其规律,然后加以实现。

很明显,从软件可重用性以及各种随之而来的好处考虑,我们要求将这个“波形显示”效果做成一个控件(Control)。

分析

还是以 FlashGet 的悬浮窗和 Windows 任务管理器作为研究对象。仔细观察它们的工作方式,发现它们有以下的共同点:

  • 在右边更新当前的波形值
  • 更新后的波形不消失,而是整体向左平移
  • 可以设置波形颜色、更新速度等

而通过深入研究,发现二者不同点如下:

  • 不同的显示方式,有曲线显示和直方图显示
  • 有无定位网格
  • 各部分颜色可自定义

 

设计

通过分析,可以决定如下:凡是二者共同点,加以重点实现;凡二者不同之处,通过设置属性(Property)进行更改。最后绘制时,基于所设置的属性,使用共同方法加以实现。

因此自定义属性如下:

  • BackColor(重写基类属性)
  • Enabled(重写)
  • ForeColor(重写)
  • GridColor 网格颜色
  • GridHeight 网格每格高度
  • GridShiftting 是否平移网格
  • GridWidth 网格每格宽度
  • Interval 波形刷新间隔(单位:毫秒)
  • Mode 波形显示方式(曲线/直方图)
  • Range 数值范围
  • ShifttingIncrement 向左平移增量
  • Value 当前值

控件因为要定时更新,因此具有一个内部的 Timer 对其进行定时,其 Interval 由控件的 Interval 属性指定。

对于此自定义控件,需要每次更新时在其 OnPaint() 事件中对整个控件进行绘制。绘制顺序为:背景 - 网格 - 波形,如此保证所有部分均正确画出且无遮挡。

从波形看,很明显,我们需要一个长度至少等于波形控件宽度的数组来存放每时刻波形的值,因此可以确定这个数组是和控件绘图画布宽度一致的。

 

算法

绘图最重要的是算法部分,如何计算如网格位置,如何将图形整体平移,如何设置波形值是本控件的重点和难点部分。

计算网格位置,以上面的 ShifttingIncrement 为 offset 参数传入

[csharp] 
  1. //网格数(不计边缘)  
  2. float div;  
  3. float pos = 0F;  
  4. //先画 垂直 方向  
  5. //可以少画一根线  
  6. div = (float)w / (float)gridWidth + 1;  
  7. for (int i = 0; i < (int)div; i++)  
  8. {  
  9.     pos += gridWidth;  
  10.     g.DrawLine(penGrid, pos - offset, 0, pos - offset, h);  
  11. }  
  12. //画 水平 方向  
  13. div = (float)h / (float)gridHeight;  
  14. pos = 0F;  
  15. for (int i = 0; i < (int)div; i++)  
  16. {  
  17.     pos += gridHeight;  
  18.     g.DrawLine(penGrid, 0, pos, w, pos);  
  19. }  
对于波形,传入其波形值数组作为参数

[csharp] 
  1. //从 0 到 w 绘制  
  2. int len = w;  
  3. //根据绘制方式  
  4. if (chartMode == StatusChart.ChartMode.Histogram)  
  5. {  
  6.     for (int i = 0; i < len; i++)  
  7.     {  
  8.         g.DrawLine(p, i, h - val[i], i, h);  
  9.     }  
  10.     g.DrawLine(p, len, h - val[len - 1], len, h);  
  11. }  
  12. else  
  13. {  
  14.     len--;  
  15.     for (int i = 0; i < len; i++)  
  16.     {  
  17.         g.DrawLine(p, i, h - val[i], i + 1, h - val[i + 1]);  
  18.     }  
  19.     len++;  
  20.     g.DrawLine(p, len - 1, h - val[len - 2], len, h - val[len - 1]);  
  21. }  
如何平移,是一个难点,需要在内部定时器的 Tick() 事件中加以处理

[csharp] 
  1. //更新网格偏移  
  2. //只有启用了网格移动才处理  
  3. if (gridShiftting)  
  4. {  
  5.     iOffset += gridShifttingIncrement;  
  6.     iOffset %= gridWidth;  
  7. }  
  8. //更新图形(整体左移)  
  9. //必须在这里而不能在画图的同时移动,  
  10. //若在画图中移动,则当画面被遮挡(OnPaint)事件不发生时无法更新  
  11. int len = w;  
  12. for (int i = 0; i < len; i++)  
  13. {  
  14.     //判断数组越界  
  15.     if (i < len - 1)  
  16.     {  
  17.         val[i] = val[i + 1];  
  18.     }  
  19.     else  
  20.     {  
  21.         val[len - 1] = currentValue;  
  22.         //break;  
  23.     }  
  24. }  
  25. //val[len] = currentValue;  
  26. Invalidate();  

最后引发控件的 Invalidate() 方法使控件重绘自身。

 

效果

最后的控件运行效果如下图所示:(用的随机数做数据,所以感觉乱跳)

 

(全文完)

你可能感兴趣的文章
basic编程命令_从BASIC到Ruby:命令行英雄的第一门编程语言的生活课
查看>>
开源教学系统_通过开源进行教学和口语学习
查看>>
2k19徽章修改_您可以修改此会议徽章
查看>>
命令行python路径命令_探索命令行英雄中Python的过去,现在和未来
查看>>
Codethink开源是入职流程的一部分
查看>>
自动部署 管道 ci cd_每个产品只有一条CI / CD管道来统治它们
查看>>
centos7实验手册_一个小时内创建一个CentOS家庭实验室
查看>>
在命令行英雄的浏览器大战中,JavaScript令人惊讶地崛起
查看>>
劳德巴赫_众包巴赫杰作的新版本
查看>>
opensource项目_Opensource.com 2014年8月报告:十大文章和网站统计信息
查看>>
moodle创建空数据库_分步指南:在Moodle上创建在线测验
查看>>
opensource项目_Opensource.com 2014年9月报告:十大文章和网站统计信息
查看>>
开源软件 商业软件_从开源软件中谋生
查看>>
项目众包 开源项目_开源是该项目负责人的第二天性
查看>>
光速不变_光速社区:开放源码新时代的最佳实践
查看>>
kubernetes 集群_使用k9s加速Kubernetes集群的管理
查看>>
fsf不推荐debian_FSF揭示了他们用于聊天,视频等的工具
查看>>
kubernetes 应用_Kubernetes如何保存我的桌面应用程序
查看>>
kubectl命令_系统管理员需要了解的9个kubectl命令
查看>>
ansible 视频_Jeff Geerling的Ansible 101视频以及更多Ansible新闻
查看>>