如何让窗口控件半透明(控件在Paint自己时,首先向主窗口询问,获取主窗口上控件所在区域的背景图)

时间:2021-07-12 23:30:40

在网上关于窗口视觉效果,有2个问题被问得最多:
第一个是如何让窗口边框有阴影效果?
第二个是如何让窗口控件有半透明效果?

对于第一个问题,我们的答案是用双层窗口模拟或是用Layered Window。
在XP下可以直接在注册窗口类时用CS_DROPSHADOW风格,系统就会自动让你的窗口附加上阴影效果,实际上系统也是通过双层窗口来实现的;当然我们也可以用自己的窗口来模拟阴影效果,只要让阴影窗口永远跟随我们的目标窗口就可以了。
如果用Layered Window, 我们只要一个窗口,通过让窗口支持Alpha通道,最后UpdateLayeredWindow, 即可实现阴影效果。

对于第二个问题,我们通常的答案是用双层窗体或是DirectUI。
因为Windows绘画是以DC为单位,而主窗口和内部控件是居于不同的DC, 所以他们不能半透明融合在一起。
另外只有Pop up的窗口才能支持Layered Window,所以只有Pop up的窗口才能实现半透明效果,所以我们通常用双层窗口来模拟控件的半透明,也就是说窗口上面的控件其实是通过一个Pop up窗口模拟出来的。所以用这种方式实现控件的半透明效果,性能比较差,也只能实现一些简单的界面。
还有一种控件半透明的方式是用DirectUI,所有的窗口控件都通过在主窗口上绘画模拟出来,实现半透明效果自然也很容易了。

所以既要实现窗口的阴影效果,又要让内部控件半透明,最终极的方式还是用WS_EX_LAYERED风格的DirectUI窗口。

其实另外还有一种既不用DirectUI,也不用双层窗口,也能实现控件半透明的方法。
该方法的关键是控件在Paint自己时,首先向主窗口询问,获取主窗口上控件所在区域的背景图,然后控件先将此背景画到自己的DC上,然后在再画自己,这样可以让控件看起来像是直接画在主窗口上一样。
当然该方法也只能针对一些简单的UI,如果子窗口层次很多,或是控件个数很多,都会有性能影响。

核心代码如下:

HBRUSH AtlGetBackgroundBrush(HWND hWnd, HWND hwndParent)
{
   CWindow wnd = hWnd;
   CWindow wndParent = hwndParent;
   CClientDC dcParent = wndParent;
   CRect rcParent;
   wndParent.GetWindowRect(&rcParent);
   CDC dcCompat1;
   dcCompat1.CreateCompatibleDC(dcParent);
   CBitmap bmpCompat1;
   bmpCompat1.CreateCompatibleBitmap(dcParent, rcParent.Width(), rcParent.Height());
   HBITMAP hOldBmp1 = dcCompat1.SelectBitmap(bmpCompat1);
   wndParent.SendMessage(WM_PRINTCLIENT, (WPARAM) (HDC) dcCompat1, (LPARAM)(PRF_ERASEBKGND | PRF_CLIENT | PRF_NONCLIENT));
   CRect rcWin;
   wnd.GetWindowRect(&rcWin);
   CDC dcCompat2;
   dcCompat2.CreateCompatibleDC();
   CBitmap bmpCompat2;
   bmpCompat2.CreateCompatibleBitmap(dcCompat1, rcWin.Width(), rcWin.Height());
   HBITMAP hOldBmp2 = dcCompat2.SelectBitmap(bmpCompat2);
   CRect rcSnap = rcWin;
   ::MapWindowPoints(NULL, wndParent, (LPPOINT) (LPRECT) &rcSnap, 2);
   dcCompat2.BitBlt(0, 0, rcWin.Width(), rcWin.Height(), dcCompat1, rcSnap.left, rcSnap.top, SRCCOPY);
   HBRUSH hBrush = ::CreatePatternBrush(bmpCompat2);
   dcCompat1.SelectBitmap(hOldBmp1);
   dcCompat2.SelectBitmap(hOldBmp2);
   return hBrush;
}

下面是一个简单的Demo:
如何让窗口控件半透明(控件在Paint自己时,首先向主窗口询问,获取主窗口上控件所在区域的背景图)

Demo源代码: TransparentControl Demo

注:Demo上的文字因为是直接用GDI画的,所以没有支持Alpha通道

http://www.cppblog.com/weiym/archive/2012/08/23/187998.html