有时候我们在使用duilib的同时,可能想自己在窗体上绘制一些图形、线条等等,但是发现绘制不上去。emmm,不用烦恼,其实是绘制上去然后又被duilib自身的绘制机制给擦掉了应该。
更多关于自绘的知识可以看另外一篇:再谈duilib控件自绘 (其中介绍了不用自定义控件,也能对某个具体的控件自身进行自绘,并且不绘制在所有控件上层的方法。)
那么正确的绘制姿势呢?
方法1:从CControlUI派生一个子类,然后直接重写DoPaint接口,在里面绘制即可。这样有1个限制,duilib默认是后添加的控件Z序高,而实际绘制全部控件的时候,Z序高的控件在上层。因此如果这个控件的Z序比较低,是有可能被其他控件盖住的,这个要看具体的布局了。
方法2:从CControlUI派生一个子类,然后直接重写DoPostPaint接口。然后调用PaintManager的AddPostPaint接口将这个控件指针加入的m_aPostPaintControls数组即可。此时在全部控件绘制完毕后,会触发DoPostPaint接口的调用,在里面绘制即可。这种方法与1的不同之处在于,PostPaint是单独的一套机制,并不影响控件的bkcolor,bkimage,text,border等绘制。并且是在所有控件绘制之后绘制的,因此它的绘制是在最上层的,不会由于控件的Z序等导致绘制被其他控件覆盖。redrain大佬有一篇控件拖拽的文章有利用,感兴趣的可以看看。
方法3:类似方法2,不过不用派生了,可以直接将根控件(通过PaintManager的GetRoot()接口可以获得,或者自己留存)通过PaintManager的AddPostPaint加入到m_aPostPaintControls里面。然后定义一个bool OnPostPaint(void* param){};然后给需要的控件通过pControl->OnPostPaint += MakeDelegate(this,&CXXXX::OnPostPaint);这样,在全部控件绘制完毕后,会触发OnPostPaint的调用,在这里绘制即可。这样与方法2显然不同的是不需要派生新控件啦。
方法3中有一个问题要注意:
在OnPostPaint中的HDC是没有的,需要自己获取才行,如果获取的是PaintManager里面的m_hDcPaint,那么是有问题的,原因是duilib是双缓冲机制,所有绘制是先绘制到m_hDcOffscreen上,再渲染到m_hDcPaint上来上屏。
在OnPostPaint中如果直接绘制到m_hDcPaint上,那么在后面上屏的时候由于m_hDcOffscreen里没有被绘制,导致被覆盖而看不到自绘的内容。同样OnPaint也会存在这个问题。
解决办法有多种选择,我这边采用给PaintManager增加GetOffscreenDC接口来处理。