• 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

  • 相关阅读:
    bzoj3884: 上帝与集合的正确用法(数论)
    洛谷10月月赛R2·浴谷八连测R3题解
    bzoj5055: 膜法师(BIT)
    bzoj2213: [Poi2011]Difference(思维题)
    bzoj1016: [JSOI2008]最小生成树计数(kruskal+dfs)
    一模 (2) day2
    一模 (2) day1
    Prime Palindromes
    常州培训 day5 解题报告
    一模 (1) day2
  • 原文地址:https://www.cnblogs.com/wucj/p/3106106.html
Copyright © 2020-2023  润新知