C#封装类似任务管理器CPU使用记录图
在逛CodeProject的时候,偶然发现了一个老外写的代码,里面有一个自定义的用户控件,类似任务管理器里面CPU使用记录的图表,如下截图:
因为自己之前没有做过这样的图表,觉得很赞,所以将这个用户控件给抠了下来,做了一个小的demo,接下来我就分析下这个用户控件的实现过程。
先看代码:
/// <summary> /// Summary description for UsageHistoryControl. /// </summary> public class UsageHistoryControl : UserControl { /// <summary> /// Required designer variable. /// </summary> private Container components = null; private const int squareWidth = 12; private const int maxLastValuesCount = 2000; private const int shiftStep = 3; private int shift = 0; private int [] lastValues1 = new int[maxLastValuesCount]; private int [] lastValues2 = new int[maxLastValuesCount]; private int nextValueIndex; private int lastValuesCount; private int max = 100; private int min = 0; public UsageHistoryControl() { // This call is required by the Windows.Forms Form Designer. InitializeComponent(); EnableDoubleBuffering(); Reset(); } public void Reset() { lastValues1.Initialize(); lastValues2.Initialize(); nextValueIndex = 0; lastValuesCount = 0; Invalidate(); } /// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if(components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Component Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { // // UsageHistoryControl // this.BackColor = System.Drawing.Color.Black; this.Name = "UsageHistoryControl"; } #endregion private void EnableDoubleBuffering() { // Set the value of the double-buffering style bits to true. SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true); UpdateStyles(); } protected override void OnResize(EventArgs e) { // Invalidate the control to get a repaint. Invalidate(); } //在任何需要引发Paint事件时,都会执行这个方法,如大小变动,invalidate()等 protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; for(int i = 0; i <= ClientRectangle.Width+squareWidth; i += squareWidth) { g.DrawLine(Pens.Green, i-shift, 0, i-shift, ClientRectangle.Height); } for(int i = 0; i < ClientRectangle.Height; i += squareWidth) { g.DrawLine(Pens.Green, 0, i, ClientRectangle.Width, i); } int startValueIndex = (nextValueIndex-1+maxLastValuesCount)%maxLastValuesCount; int prevVal1 = GetRelativeValue(lastValues1[startValueIndex]); int prevVal2 = GetRelativeValue(lastValues2[startValueIndex]); for(int i = 1; i < lastValuesCount; ++i) { int index = nextValueIndex - 1 - i; if (index < 0) { index += maxLastValuesCount; } int val1 = GetRelativeValue(lastValues1[index]); int val2 = GetRelativeValue(lastValues2[index]); g.DrawLine( Pens.Red, ClientRectangle.Width - (i - 1) * shiftStep, ClientRectangle.Height - prevVal2, ClientRectangle.Width - i * shiftStep, ClientRectangle.Height - val2); g.DrawLine( Pens.LawnGreen, ClientRectangle.Width-(i-1)*shiftStep, ClientRectangle.Height-prevVal1, ClientRectangle.Width-i*shiftStep, ClientRectangle.Height-val1); prevVal1 = val1; prevVal2 = val2; } } private int GetRelativeValue(int val) { int result = val * (ClientRectangle.Height-2) / max + 1; return result; } public void AddValues(int val1,int val2) { lastValues1[nextValueIndex] = val1; lastValues2[nextValueIndex] = val2; nextValueIndex++; nextValueIndex %= maxLastValuesCount; lastValuesCount++; if (lastValuesCount > maxLastValuesCount) { lastValuesCount = maxLastValuesCount; } shift += shiftStep; //shift %= squareWidth; shift = shift % squareWidth; Invalidate(); } public int Maximum { get { return max; } set { // Make sure that the maximum value is never set lower than the minimum value. if (value < min) { min = value; } max = value; // Invalidate the control to get a repaint. Invalidate(); } } }
注意到构造函数中的方法“EnableDoubleBuffering() ”,它的作用是启用GDI+的双缓冲,可以有效防止界面在重绘的时候发生闪烁。关于双缓冲的介绍可以参考这里:http://www.cnblogs.com/bnuvincent/archive/2009/08/04/1538484.html,http://blog.csdn.net/gisfarmer/article/details/4366707。另一个方法Reset() 就是初始化一些变量,不过其中值得一提的就是”值类型的默认构造函数“,这个大家平时可能很少见,最多的就是引用类型的构造函数,比喻说类。
具体实现画线的方法就是重写了基类的OnPaint方法,因为在实现双缓冲的时候必须要重写这个方法。该方法先是重绘原生的界面,上面没有任何的线条,然后在更具传进来的参数画线,不过这里
for(int i = 1; i < lastValuesCount; ++i){}
感觉不是很好,随着lastValuesCount的值越来越大,每次循环的次数就越来越多了,因为从未做过类似的事情,不知画图是不是真的这样?
具体调用就很简单了,因为要实现类似CPU那样的使用记录,所以在调用界面上拖了一个时间控件,在该控件的Tick事件里面传值
private void timer1_Tick(object sender, EventArgs e) { Random rand = new Random(); this.usageHistoryControl1.AddValues(rand.Next(100),rand.Next(50,100)); }
哦,原来这个图是这样实现的,想当初学习GDI+的时候,就怎么没有想到做一个这样的Demo了?
老外文章地址:http://www.codeproject.com/Articles/7933/Smart-Thread-Pool
示例代码下载:https://files.cnblogs.com/wucj/DrawLine.rar