给软件换肤,滚动条通常是个雷区,一般就跳过了。因为不但要给自己的控件绘制滚动条,还要给微软的控件绘制滚动条。当我们用到ComboBox/ListCtrl/Tree甚至是IE控件和第三方控件的时候,头自然就大了。所以网上一直有“自绘难莫过滚动条”的说法,看了一些帖子,下载了一些代码,学习一下:)
我用CListCtrl做了个试验。一开始,我就发现滚动条不是独立的窗口,调用GetScrollBarCtrl自然无法得到任何结果。通过截获控件的WM_NCPAINT消息,确定它在非客户区绘制了滚动条。然后我替换了控件的WindowProc处理函数,在相关函数处理后,再重绘滚动条。结果看起来貌似可以,但有两点遗憾:一是滚动条有不明显的闪动,二是用鼠标拖拽滚动条滑块的时候,还是会出现系统默认的滚动条。原来滚动条的拖动是在uxtheme.dll里处理的(winXP),比较正统的处理办法是截获相关函数调用重绘(HOOK达不到这一点,要用一点简单的汇编才行)。考虑到老的win98/2000等,还是算了吧,再说winXP也不年轻了,呵呵。本想放手,抱着最后的一线希望:如果拖动的时候把滚动条挖掉,用桌面DC重绘可不可以呢,但还要求在挖掉的区域又反应?这要看微软的机制给不给面子。用SPY++可以观察到拖动调用了WM_SYSCOMMAND,可以截获。拖动过程中调用了WM_VSCROLL,仍然要处理。结果还算不失望,于是第二个bug解决了。
另外还有第二种方法:给控件加一个CScrollBar子窗口,覆盖到滚动条的位置。这也是目前普遍用到的办法。使用CScrollBar比较复杂(和内置滚动条相比较),滚动条和视图之间的同步要自己处理。但这种滚动条实现自绘比较简单,通常子类化就可以了。所以这种方法对于自己实现的特殊控件,比较容易;而对于系统内置的滚动条,就鞭长莫及了。
总结一下:
1.微软的滚动条有两种:一种是从CWnd继承而来的CScrollBar,为方便用户提供的一个控件,和普通控件一样,可以自绘;另外一种是内置的滚动条,主要用于View系列和控件,对其功能进行了封装,没有提供介入的接口。
2.自绘滚动条有上面提到的两种方法:替换WindowProc函数或者添加滚动条控件。
3.网上的例子基本是添加的子窗口,coolsb是比较强大的一个库,用C写的而不是C++,但有个bug,就是我上面提到的拖动。
4.今天CSDN上有人新发布了源码(李俊 skinsb.lib),相当的好,方法和coolsb第二种方法一样。极力推荐。
http://download.csdn.net/source/1013411
附注(对于内建的滚动条):
理想的方案:让系统去计算,而在显示的时候自绘。但画控制条的时候,不只有NCPaint的时候,有些系统函数里直接绘制了,不通过消息。也就找不到一个合适的控制点,取截获所有的系统绘制,结果就是闪动(自己绘制和系统绘制交替)。
coolsb有两套方法,一是coolsb.lib,二是coolsb_detours.lib。基本思路都是截获系统默认信息通路,不让系统知道滚动条信息(这样系统不会绘制,也就不会闪动),自己处理计算、存储滚动条的大小和位置,并在非客户区绘制滚动条工作。不同的是coolsb.lib截获WM_VSCROLL WM_HSCROLL WM_SIZE消息,而coolsb_detours.lib截获的是Get/Set ScrollInfo Get/Set ScrollPos Get/Set ScrollPos ShowScrollBar等一系列API函数。这种控件只能个别对待,对于所有的tree、listview、combox、edit等控件,还要一个一个做标识。
coolsb.lib提供了test和mfctest。test贴图滚动条的bug:放大到没有滚动条的时候,其实还是有滚动条的,可以拖动开始初,会显现出系统滚动条。mfctest没有贴图滚动条。coolsb_detours.lib只在winNT、win2000、winXP系统下可用。