前两天由于需要用CSharp做一个控件,就翻看了以前做控件里自己的代码。不看不知道,看了才知道以前自己写的控件糟糕到什么程度!
自定义一个控件需要继承CSharp提供的一个专门的用户控件类UserControl,我们使用自己的控件基本上或者说必须重载一下onPaint方法了,以前的代码就是将绘制控件所需要的逻辑都写在onPaint方法里了,如果绘图量比较小的话还可以接受,但是稍微复杂一点的控件这样做的弊端就出来了!这里会将导致整个窗体的刷新拖慢从而使界面不流畅!
不清楚CSharp控件的新手容易犯这个错误,将onPaint变得极其庞大。大家都知道,内存与内存之间传递数据很快,但是内存与显存之间交换数据相较于内存与内存之间速度就会很慢了,不知道.NET对这一部分是如何优化的,但是直接在onPaint里每次调用Graphics的绘制方法直接操作显存(姑且这么说),将数据一小段地从内存中传递到显存中这个速度是很慢的。
所以假如的代码是这样的
protected override void onPaint(...)
{
e.g.draw...();
...
...
e.g.draw...();
...
...
}
那么建议改掉它!
我目前所做的控件里的onPaint是这样的
protected override void onPaint(...)
{
base.onPaint(e);
e.Graphics.DrawImage(this.map,0,0);
}
绘图逻辑只有一句drawImage(this.map,0,0);
这一点借鉴与许多图形库里都有的缓冲区思想,将要显示的内容先绘制在内存里的缓冲区中(快),需要显示的时候把这块缓冲区绘制出来(直接传递一大块数据,快)
也即是控件里首先我创建了一个缓冲区
Bitmap map=new Bitmap(this.Width,this.Height);
说白了就是一个位图,只不过这个位图所在的位置在内存中。这张图片与我控件的大小相等,所以onPaint的时候刚好显示的就是这张位图。
那么我的绘图操作就是这样的。(不在onPaint里)
Graphics g=Graphics.fromImage(this.map);
g.draw...();
...
g.draw...();
g这个对象会将绘制的图形绘制到我提供的map上,由于这里所有的步骤都是在内存中进行的,所以这个步骤相较于前面快就能绘制完成。
整个控件里所有的绘图操作都绘制到map上,需要显示的时候直接将map绘制到真正会显示的界面中,这就是我的主要思想。
速度上我没有实际进行测量,不过使用时可以明显地看到这两者的差异。
---------------------
另外设置以下几个标志位,开启默认的DoubleBuffer和显示告诉控件是用于自己绘制
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
前几天看到原始代码时我毫不犹豫立马将控件重写了,而这次重写竟然一个小时都不到就完成了全部工作依,稀记得原始的代码自己琢磨了好久才完成的,看来最能提高还是不断地敲代码阿。
更快的绘制方式,我现在想到的只有从c++那方边突破了,如果有更好的方式还请告知。
这星期天气无常,好天气快点来吧。
来源:oschina
链接:https://my.oschina.net/u/870108/blog/224192