最近要实现一个显示系统流量的功能,于是想到了图表。功能是可以动态地显示某个时间点的值,并可以像迅雷的流量显示那样可以随着时间向左移动。
全部的代码如下:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Windows.Forms; using System.Collections; namespace TestApp { public partial class GraphDrawer : UserControl { private bool isDyanmic=false; //动态or静态 0:1 private int lineType = 0; //图表类型: 0--线型 2--柱型 private int nodeType = 0; //线形时节点的表示符 private bool isShowNodeValue = true; //是否显示各节点值 private Point startPoint; //显示的起始点 private bool isShowNegative = false; //是否显示负值区 private int gapX = 5; //X轴各点间距(单位像素) private int gapY = 5; //Y轴各点间距(单位像素) private int valueType = 1; //数据类型 private int gapValueX = 10; //X轴的数值差 private int gapValueY = 10; //Y轴的数值差 private int splitType = 1; //X,Y轴的分隔符的类型:0--无 1--短线 2--圆点 3--长虚线 4--长实线 private int xylineWidth = 3; //X,Y轴线的宽度 private int yStartValue = 0; //Y轴上的起始值 private Color valueLineColor = Color.Red; //值线的颜色 private Color xLineColor = Color.Blue; //X轴的颜色 private Color yLineColor = Color.Blue; //Y轴的颜色 private Color nodeTextColor = Color.Black; //节点文字颜色 private Font nodeTextFont = null; //节点文字配置 private TimeType timeNodeTextType; //X轴节点为时间型时,所要显示的时间类型 private ArrayList xNodeList = null; //X轴的节点组 private ArrayList yNodeList = null; //Y轴的节点组 private ArrayList valueNodeList = null; //值点组 private Graphics drawPanel; private DataSequence sequence = new DataSequence(); public Point StartPoint { get { return this.startPoint; } set { this.startPoint = value; } } public int GapX { get { return this.gapX; } set { this.gapX = value; } } public int GapY { get { return this.gapY; } set { this.gapY = value; } } public Color ValueLineColor { get { return this.valueLineColor; } set { this.valueLineColor = value; } } public Color XLineColor { get { return this.xLineColor; } set { this.xLineColor = value; } } public Color YLineColor { get { return this.yLineColor; } set { this.yLineColor = value; } } public Color NodeTextColor { get { return this.nodeTextColor; } set { this.nodeTextColor = value; } } public Font NodeTextFont { get { return this.nodeTextFont; } set { this.nodeTextFont = value; } } public bool IsShowNodeValue { get { return this.isShowNodeValue; } set { this.isShowNodeValue = value; } } public TimeType TimeNodeTextType { get { return this.timeNodeTextType; } set { this.timeNodeTextType = value; } } public GraphDrawer() { InitializeComponent(); this.startPoint = new Point(20,this.Height-10); } public void InitBaseLine() { drawPanel= this.CreateGraphics(); drawPanel.Clear(Color.White); Pen myPen = new Pen(this.xLineColor,xylineWidth); //画XY轴 drawPanel.DrawLine(myPen,startPoint.X,startPoint.Y,this.Width,this.startPoint.Y); myPen.Color = this.yLineColor; drawPanel.DrawLine(myPen,startPoint.X,startPoint.Y,startPoint.X,2); drawSplitLine(); } /// <summary> /// 画分隔符 /// </summary> private void drawSplitLine() { int xPosition = startPoint.X; int yPosition = startPoint.Y; Pen myPen = new Pen(xLineColor); xNodeList = new ArrayList(); yNodeList = new ArrayList(); myPen.Width = 1; myPen.Color = xLineColor; //X轴上分隔符 do { xPosition += gapX; drawPanel.DrawLine(myPen, xPosition, this.startPoint.Y - 4, xPosition, this.startPoint.Y); Point xp = new Point(xPosition,startPoint.Y); xNodeList.Add(xp); } while (xPosition <= this.Width - xylineWidth); //Y轴上分隔符 do { yPosition -= gapY; drawPanel.DrawLine(myPen,startPoint.X, yPosition, startPoint.X + 4, yPosition); Point yp = new Point(startPoint.X,yPosition); yNodeList.Add(yp); } while (yPosition >= 0); } /// <summary> /// 写各节点值(双数值型),用于静态时 /// </summary> /// <param name="xStartValue">X轴的起始值</param> /// <param name="yStartValue">Y轴的起始值</param> private void writeNodeText(int xStartValue,int yStartValue) { if (this.isShowNodeValue) { int xPosition = startPoint.X; int yPosition = startPoint.Y; int xCurrentValue = xStartValue; int yCurrentValue = yStartValue; SolidBrush txtBrush = new SolidBrush(this.nodeTextColor); while (xPosition <= this.Width - xylineWidth) { xCurrentValue += gapValueX; xPosition += gapX; drawPanel.DrawString(xCurrentValue+"", nodeTextFont, txtBrush, xPosition, startPoint.Y +2); } while (yPosition > gapY) { yCurrentValue += gapValueY; yPosition -= gapY; drawPanel.DrawString(yCurrentValue+"", nodeTextFont, txtBrush, startPoint.X - 20, yPosition-5); } } } /// <summary> /// 写各节点值(时间数值型),用于静态时 /// </summary> /// <param name="xStartTime">X轴的起始时间</param> /// <param name="yStartValue">Y轴的起始值</param> private void writeNodeText(DateTime xStartTime,int yStartValue) { if(this.isShowNodeValue) { } } /// <summary> /// 写XY轴的各节点值,动态情况下 /// </summary> /// <param name="p">位置,一般是XY轴上的节点</param> /// <param name="str">字符</param> private void writeNodeText(Point p,string str) { SolidBrush txtBrush = new SolidBrush(this.nodeTextColor); drawPanel.DrawString(str, nodeTextFont, txtBrush, new Point(p.X-str.Length*2,p.Y)); } /// <summary> /// 生成值点 /// </summary> /// <param name="p">X轴上节点对应的Point</param> /// <param name="theValue">值</param> private Point writeValueNode(Point p, int theValue) { int yPosition = startPoint.Y - (theValue / gapValueY * gapY); //值对应的Y轴位置 return new Point(p.X, yPosition); } /// <summary> /// 用现有的值点组生成线图 /// </summary> private void connectNodesToFormLine() { int valueNodeNum = valueNodeList.Count; Pen linePen = new Pen(this.valueLineColor); if (valueNodeNum > 1) { for (int i = 0; i < valueNodeNum; i++) { drawPanel.DrawLines(linePen, (Point[])valueNodeList.ToArray(typeof(Point))); } } } /// <summary> /// 在值点画一个标记 /// </summary> /// <param name="p"></param> private void drawValueNode(Point p) { drawPanel.FillEllipse(new SolidBrush(Color.Blue), p.X - 2, p.Y - 2, 4, 4); } /// <summary> /// 写节点文字;生成值点组,并将值点组连成线 /// </summary> /// <param name="seq">对应的队列</param> public void drawLine(DataSequence seq) { int nodeNum = xNodeList.Count; int yPosition = startPoint.Y; int yCurrentValue = yStartValue; SolidBrush txtBrush = new SolidBrush(this.nodeTextColor); valueNodeList = new ArrayList(); if (seq.Length >= nodeNum) { for (int i = 0; i < nodeNum; i++) { Point vp = writeValueNode((Point)xNodeList[i], seq.getValueIndexOf(i)); valueNodeList.Add(vp); //将生成的值点加入到组中 writeNodeText((Point)xNodeList[i], seq.getTimeStrIndexOf(timeNodeTextType, i)); } } else { for (int i = 0; i < seq.Length; i++) { Point vp = writeValueNode((Point)xNodeList[i], seq.getValueIndexOf(i)); valueNodeList.Add(vp); //将生成的值点加入到组中 drawValueNode(vp); writeNodeText((Point)xNodeList[i], seq.getTimeStrIndexOf(timeNodeTextType, i)); } } connectNodesToFormLine(); while (yPosition > gapY) { yCurrentValue += gapValueY; yPosition -= gapY; drawPanel.DrawString(yCurrentValue + "", nodeTextFont, txtBrush, startPoint.X - 20, yPosition - 5); } } /// <summary> /// 画动态图 /// </summary> /// <param name="dt"></param> /// <param name="val"></param> public void drawDynamic(DateTime dt,int val) { sequence.addNode(dt,val); InitBaseLine(); drawLine(this.sequence); } public void clearGraph() { drawPanel.Clear(Color.White); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); this.InitBaseLine(); drawLine(this.sequence); } } }
目前没有考虑的太多,只是接收一个"时间"和"值"的参数,加入到要显示的队列后面。再显示出来。
队列的实现用了双链表:
using System; using System.Collections.Generic; using System.Text; namespace TestApp { public enum TimeType { all, year, month, day, hour, minutes, seconds, year_month_day, hour_minute_second } /// <summary> /// 使用双键表来实现队列 /// </summary> public class DataSequence { DataNode head = null; //队头指针 DataNode tail = null; //队尾指针 DataNode here = null; //游标 int maxLength = 10; //最大长度 int length = 0; //目前长度 public int Length { get { return this.length; } } public int MaxLength { get { return this.maxLength; } set { this.maxLength = value; } } /// <summary> /// 构造空队列 /// </summary> public DataSequence() { } /// <summary> /// 构造只有一个节点的队列 /// </summary> /// <param name="theTime"></param> /// <param name="theValue"></param> public DataSequence(DateTime theTime,int theValue) { DataNode dn = new DataNode(theTime,theValue); head = dn; tail = dn; here = dn; length = 0; } /// <summary> /// 根据传进来的二维字符串数组构造队列 /// </summary> /// <param name="nodelist">二维字符串,其中第一行为时间,第二行为值</param> public DataSequence(string [][] nodelist) { } /// <summary> /// 新增一个节点,在超出最大长度时将队头元素抛掉 /// </summary> public void addNode(DateTime theTime,int theValue) { DataNode dn = new DataNode(theTime,theValue); if (length > 0) { if (length < maxLength) { tail.Next = dn; dn.Front = tail; tail = dn; length++; } else { head = head.Next; head.Front.Next = null; head.Front = null; tail.Next = dn; dn.Front = tail; tail = dn; } } else { head = dn; tail = dn; here = dn; length++; } } /// <summary> /// 按顺序获得所有队列内容 /// </summary> /// <param name="type">要获取的时间类型</param> /// <returns>包含时间和值的二维字符串数组,其中第一行为时间,第二行为值</returns> public string[,] getValueList(TimeType type) { string [,] dataArry = new string[length,2]; here=head; if (length > 0) { for (int i = 0; i < length; i++) { dataArry[i, 1] = getSubTimeString(type, here); dataArry[i, 2] = here.V_value.ToString(); here = here.Next; } here = head; return dataArry; } else { return null; } } /// <summary> /// 按Index获取指定类型时间字符串 /// </summary> /// <param name="ttype"></param> /// <param name="index"></param> /// <returns></returns> public string getTimeStrIndexOf(TimeType ttype,int index) { if (length > 0 && length >= (index + 1)) { here = head; for (int i = 0; i < index; i++) { here = here.Next; } return getSubTimeString(ttype, here); } else { return "index out range"; } } public int getValueIndexOf(int index) { if (length > 0 && length >= (index + 1)) { here = head; for (int i = 0; i < index; i++) { here = here.Next; } return here.V_value; } else { return 0; } } /// <summary> /// 清空队列 /// </summary> public void clearSequence() { this.head = null; this.tail = null; this.here = null; length = 0; GC.Collect(); } /// <summary> /// /// </summary> /// <param name="type"></param> /// <param name="node"></param> /// <returns></returns> private string getSubTimeString(TimeType type,DataNode node) { string timeSubString = null; switch (type) { case TimeType.all: timeSubString = node.getTime(); break; case TimeType.year: timeSubString = node.getYear(); break; case TimeType.month: timeSubString = node.getMonth(); break; case TimeType.day: timeSubString = node.getDay(); break; case TimeType.hour: timeSubString = node.getHour(); break; case TimeType.minutes: timeSubString = node.getMinutes(); break; case TimeType.seconds: timeSubString = node.getSeconds(); break; case TimeType.year_month_day: timeSubString = node.getYearMonthAndDay(); break; case TimeType.hour_minute_second: timeSubString = node.getHourMinuteAndSecond(); break; default: timeSubString = node.getTime(); break; } return timeSubString; } } /// <summary> /// 队列中的节点 /// </summary> class DataNode { DataNode front=null; DateTime v_time; int v_value; int v_num; //备用的编号 DataNode next=null; public DataNode Front { get { return front; } set { this.front = value; } } public DataNode Next { get { return next; } set { this.next = value; } } public DateTime V_time { get { return this.v_time; } set { this.v_time = value; } } public int V_value { get { return this.v_value; } set { this.v_value = value; } } public int V_num { get { return this.v_num; } } public DataNode() { } public DataNode(DateTime dt,int val) { this.v_time = dt; this.v_value = val; } public string getTime() { string time = null; if (!string.IsNullOrEmpty(v_time.ToString())) { time = this.v_time.ToString(); } else { time = "null"; } return time; } public string getYear() { string year=null; if (!string.IsNullOrEmpty(v_time.ToString())) { year = this.v_time.ToString().Substring(0, 4); } else { year = "null"; } return year; } public string getMonth() { string month = null; if (!string.IsNullOrEmpty(v_time.ToString())) { month = this.v_time.ToString().Substring(5, 2); } else { month = "null"; } return month; } public string getDay() { string day = null; if (!string.IsNullOrEmpty(v_time.ToString())) { day = this.v_time.ToString().Substring(8, 2); } else { day = "null"; } return day; } public string getHour() { string hour = null; if (!string.IsNullOrEmpty(v_time.ToString())) { hour = this.v_time.ToString().Substring(11, 2); } else { hour = "null"; } return hour; } public string getMinutes() { string minute = null; if (!string.IsNullOrEmpty(v_time.ToString())) { minute = this.v_time.ToString().Substring(14, 2); } else { minute = "null"; } return minute; } public string getSeconds() { string second = null; if (!string.IsNullOrEmpty(v_time.ToString())) { second = this.v_time.ToString().Substring(17, 2); } else { second = "null"; } return second; } public string getYearMonthAndDay() { string ymd = null; if (!string.IsNullOrEmpty(v_time.ToString())) { ymd = this.v_time.ToString().Substring(0, 10); } else { ymd = "null"; } return ymd; } public string getHourMinuteAndSecond() { string hms = null; if (!string.IsNullOrEmpty(v_time.ToString())) { hms = this.v_time.ToString().Substring(11, 8); } else { hms = "null"; } return hms; } } }
最终的效果与测试代码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Collections; namespace TestApp { public partial class LineForm : Form { private Timer tm1; public LineForm() { InitializeComponent(); tm1 = new Timer(); tm1.Interval = 1000; tm1.Tick += new EventHandler(tm1_Tick); } void tm1_Tick(object sender, EventArgs e) { Random rd= new Random(); int num= rd.Next(1,100); this.graphDrawer1.drawDynamic(DateTime.Now,num); } /// <summary> /// 开始 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { tm1.Start(); } /// <summary> /// 暂停 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click(object sender, EventArgs e) { tm1.Stop(); } } }
代码比较乱,有待改善。
posted on 2009-10-21 16:40 心笑峰 阅读(178) 评论(0) 编辑 收藏 举报