问题描述
做一个项目,使用C#GDI画图,以达到可视化目的,使用自定义的控件,整个控件通过CreateGraphics()来绘图,数据存放在DataTable有几万行的数据,需要用多列(有十几列,根据不同需要来决定使用不同的列绘图)来绘图,使用GDI如下:publicvoidPaintGraph(RectangledrawRectangle){lock(refresh_lock){BufferedGraphicsContextcurrentContext=BufferedGraphicsManager.Current;BufferedGraphicsmyBuffer=currentContext.Allocate(this.CreateGraphics(),drawRectangle);Graphicsg=myBuffer.Graphics;//绘制边框和座标轴图像DrawBackGround(g);//画线条DrawSeries(g);//画十字线if(showCrossHair){DrawCrossHair(g);}myBuffer.Render();myBuffer.Dispose();}}
在DrawSeries(g)时如下(Data是PointF(,)类型),由于会经常会使用方向向上进行放大,向下进行缩小;使用方向键向左或向右快速查看上一行或下一行的数据的一些信息(绘制在旁边)。每次放大或缩小时,座标轴需要根据当前的数据识别最大值和最小值,然后将每个数据重新计算出新的座标,再画图;每次向左或向右时,若有出现新的数据,也需要根据当前的数据识别最大值和最小值,然后将每个数据重新计算出新的座标,再画图;for(intlsJ=0;lsJ<LineSeriesList.Count;lsJ++){LineSeriesls=LineSeriesList[lsJ];for(inti=0;i<ls.Data.Count()-1;i++){g.DrawLine(ls.LinePen,ls.Data[i],ls.Data[i+1]);}}
问题是,使用方向键连续地放大、缩小,或者连续地向左或向右的时候,刷新会很卡,有时方向键停下来,控件还会延时3-5秒左右才显示完,有什么方法可以提示效率吗?
解决方案
解决方案二:
进行抽样,或者找出画图的数据范围,只画一部分。如果数据是连续的,在抽样的时候还可以取个均值
解决方案三:
你不可能把几万个数据都都画在屏幕上,即使画上了,字迹也会小的看不清(没有实用价值)所以你实际只去了一小部分数据进行展示,因此说原始数据有多么多么多是没有意义的我不知道你那些自定义方法内部是怎么做的仅就方向键控制放大缩小而言,应该是将图元绘制在GraphicsPath中,通过仿射矩阵(Matrix)完成的
解决方案四:
引用1楼johnliuyuan的回复:
进行抽样,或者找出画图的数据范围,只画一部分。如果数据是连续的,在抽样的时候还可以取个均值
因是数据分析,希望看到实际的数据变化情况,进行抽样是个不错的方向,也有试用,基本上把3万条数据抽样成4000左右(图形看起来不会失真太严重),如果条线少还好,有时同一个界面,可能会存在10几条折线(对比不同系列,及不同指标),还是没办法解决这个问题。
解决方案五:
引用2楼xuzuning的回复:
你不可能把几万个数据都都画在屏幕上,即使画上了,字迹也会小的看不清(没有实用价值)所以你实际只去了一小部分数据进行展示,因此说原始数据有多么多么多是没有意义的我不知道你那些自定义方法内部是怎么做的仅就方向键控制放大缩小而言,应该是将图元绘制在GraphicsPath中,通过仿射矩阵(Matrix)完成的
使用GraphicsPath,在放大和缩小时,因要重新计算所有的GraphicsPath,效率变差了;在向左和向右移,只要不需重新计算GraphicsPath,效率确实提高很多,在一般的移动十字线不会有延时感。Matrix的问题正在学习,因是放大或缩小,所有的像素位置都要重新计算,而且数据区间会不一样,感觉Matrix不一定能用得上,今天先试试。谢谢。
解决方案六:
现在的计算机的性能远没有这么高。满屏幕1440*960想全部填充文字都反应不过的。但还有一群程序员,写的代码随意的浪费服务器资源。你这个只有想折中的方法了,不全部显示。或者平滑过度之类的。
解决方案七:
1.绘图用控件的OnPaint或者Paint事件,不要自己创建画布,频繁的创建,影响效率2.考虑使用图片,预生成,或者缓存部分绘图3.抽样或者曲线拟合,对数据进行预处理或者干脆用控件算了,比如TeeChart
解决方案八:
引用6楼assky124的回复:
1.绘图用控件的OnPaint或者Paint事件,不要自己创建画布,频繁的创建,影响效率2.考虑使用图片,预生成,或者缓存部分绘图3.抽样或者曲线拟合,对数据进行预处理或者干脆用控件算了,比如TeeChart
显示内容只能自定义,因为会有太多细节需要自己来定义。现在的问题是放大和缩小时,区间不固定,没办法使用预生成。
解决方案九:
谁还有什么好的建议吗?
解决方案十:
假设点击一次放大或缩小需要耗费1s进行绘制如果你连续点击4次,可能需要4s进行绘制但此时前3次绘制是没有意义的,只需要1s做最后一次的绘制这个细节你考虑了没?(从你所说的“方向键连续地放大、缩小,或者连续地向左或向右的时候,刷新会很卡”,推测可能没有考虑)
解决方案十一:
引用9楼wzn721721的回复:
假设点击一次放大或缩小需要耗费1s进行绘制如果你连续点击4次,可能需要4s进行绘制但此时前3次绘制是没有意义的,只需要1s做最后一次的绘制这个细节你考虑了没?(从你所说的“方向键连续地放大、缩小,或者连续地向左或向右的时候,刷新会很卡”,推测可能没有考虑)
怎么去判断和提前中断呢?
解决方案十二:
使用独立的线程去绘制每次要绘制时判断绘制线程是否执行完毕若未完毕直接终止线程重新绘制
解决方案十三:
引用11楼wzn721721的回复:
使用独立的线程去绘制每次要绘制时判断绘制线程是否执行完毕若未完毕直接终止线程重新绘制
可以试下,谢谢。
解决方案十四:
1.自己的usercontrol在构造函数里加上双缓冲-true;2.drawline当然慢了,要drawLines,类似polyline,首先就会快几倍。3.所有的绘制代码都应该放在onpaint里,其他事件里只是调用invalidate;4.裁剪clip:只绘制无效区内部的部分。滚动的时候每次都是增加一点点无效区而已。采用1、2、3点后,你的绘图效果应该就可以接受了。采用4点后就基本不会有任何问题了。多线程、拟合之类的绘几十万上百万点的时候再考虑就行了。这么点数据真不需要多线程(当然有也更好)。
解决方案十五:
引用13楼wk_knife的回复:
1.自己的usercontrol在构造函数里加上双缓冲-true;2.drawline当然慢了,要drawLines,类似polyline,首先就会快几倍。3.所有的绘制代码都应该放在onpaint里,其他事件里只是调用invalidate;4.裁剪clip:只绘制无效区内部的部分。滚动的时候每次都是增加一点点无效区而已。采用1、2、3点后,你的绘图效果应该就可以接受了。采用4点后就基本不会有任何问题了。多线程、拟合之类的绘几十万上百万点的时候再考虑就行了。这么点数据真不需要多线程(当然有也更好)。
谢谢。