惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
爱范儿
爱范儿
博客园_首页
博客园 - 【当耐特】
V
Visual Studio Blog
博客园 - 叶小钗
月光博客
月光博客
美团技术团队
J
Java Code Geeks
小众软件
小众软件
Y
Y Combinator Blog
博客园 - Franky
Martin Fowler
Martin Fowler
博客园 - 聂微东
Microsoft Azure Blog
Microsoft Azure Blog
IT之家
IT之家
MyScale Blog
MyScale Blog
人人都是产品经理
人人都是产品经理
Microsoft Security Blog
Microsoft Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
阮一峰的网络日志
阮一峰的网络日志
酷 壳 – CoolShell
酷 壳 – CoolShell
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
云风的 BLOG
云风的 BLOG

博客园 - icwin

在LINUX下进行C语言编程所需要的基础知识 路由器的“心脏”技术 软件费用的组成 如何利用C#创建和调用DLL收藏 如何利用C#创建和调用DLL | C#中创建和使用资源动态链接库收藏 C#调用API的一些基础(希望有人用的上) 如何合理创建c#对象 MD5加密简介以及C#下的操作实现 【转】C#项目打包,并自动安装SQL数据库 手把手教你用C#打包应用程序(安装程序)【卸载模块已添加】 C#.NET中存取数据库入门贴(ADO.NET) http://bbs.bccn.net/ .NET开发中的一些小技巧收藏 C#读取Excel数据收藏 C#文件夹和文件的常见操作收藏 C#实现重启应用程序收藏 C#实现SQL数据库备份与恢复 C#备份和还原SQL数据库
Nobi's StatusChart - 野比的状态波形图控件
icwin · 2008-09-06 · via 博客园 - icwin

Nobi's StatusChart - 野比的状态波形图控件
从构思到实现

野比 著

源程序下载:
Demo StatusChartDemo.rar (467.76 KB)

控件 StatusChart.rar (51.69 KB)

背景

目前比较流行的 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 参数传入

复制内容到剪贴板

代码:
            //网格数(不计边缘)
            float div;
            float pos = 0F;
            //先画 垂直 方向
            //可以少画一根线
            div = (float)w / (float)gridWidth + 1;
            for (int i = 0; i < (int)div; i++)
            {
                pos += gridWidth;
                g.DrawLine(penGrid, pos - offset, 0, pos - offset, h);
            }
            //画 水平 方向
            div = (float)h / (float)gridHeight;
            pos = 0F;
            for (int i = 0; i < (int)div; i++)
            {
                pos += gridHeight;
                g.DrawLine(penGrid, 0, pos, w, pos);
            }

对于波形,传入其波形值数组作为参数

复制内容到剪贴板

代码:
            //从 0 到 w 绘制
            int len = w;
            //根据绘制方式
            if (chartMode == StatusChart.ChartMode.Histogram)
            {
                for (int i = 0; i < len; i++)
                {
                    g.DrawLine(p, i, h - val[i], i, h);
                }
                g.DrawLine(p, len, h - val[len - 1], len, h);
            }
            else
            {
                len--;
                for (int i = 0; i < len; i++)
                {
                    g.DrawLine(p, i, h - val[i], i + 1, h - val[i + 1]);
                }
                len++;
                g.DrawLine(p, len - 1, h - val[len - 2], len, h - val[len - 1]);
            }

如何平移,是一个难点,需要在内部定时器的 Tick() 事件中加以处理

复制内容到剪贴板

代码:
            //更新网格偏移
            //只有启用了网格移动才处理
            if (gridShiftting)
            {
                iOffset += gridShifttingIncrement;
                iOffset %= gridWidth;
            }
            //更新图形(整体左移)
            //必须在这里而不能在画图的同时移动,
            //若在画图中移动,则当画面被遮挡(OnPaint)事件不发生时无法更新
            int len = w;
            for (int i = 0; i < len; i++)
            {
                //判断数组越界
                if (i < len - 1)
                {
                    val[i] = val[i + 1];
                }
                else
                {
                    val[len - 1] = currentValue;
                    //break;
                }
            }
            //val[len] = currentValue;
            Invalidate();

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

效果

最后的控件运行效果如下图所示:

总结

通过设计并实现 StatusChart 控件,我们研究了波形状态监控的相关现象和原理,复习并使用了控件的设计生成,最后使用 GDI+ 技术对其进行了实现。

声明

本控件仅供,学习交流,请勿用于商业用途或其他一切正式场合。

作者野比拒绝对因擅用本控件所造成的一切法律或社会的不良后果负责。
For evaluation ONLY, NOT for commercial use!

如果你的程序用到了本控件或其部分思路,请发一份给我,让我也能分享到你的成就!

[ 本帖最后由 野比 于 2008-4-22 21:44 编辑 ]