方法一、系统属性:
为了实现阴影效果,这两天google了不少,终于从 SysShadow 中找到了一点线索。
给窗口添加阴影:
SetClassLong(this->m_hWnd, GCL_STYLE, GetClassLong(this->m_hWnd, GCL_STYLE) | CS_DROPSHADOW);
在OnCreate(CmainFrame)或者OnInitDialog(Cdialog)里面添加即可,该属性与是否选中系统的“显示菜单下面的阴影”有关系。不规则窗口(SetWindowRgn)一样可以将显示不规则阴影
方法二、分层窗口
采用 WS_EX_LAYERED 扩展属性 + UpdateLayeredWindow + PNG图片 + GdiPlus 可以实现很强大的不规则窗体,而不用像利用 BMP 图去抠每一个像素点。不过利用分层窗口实现有一个很大的缺陷,就是分层窗口不能是子窗口,分层之后的窗口的子控件(包括子窗口)不会再显示了,虽然控件仍然可以响应到消息。
解决办法有两个:
1. 附加一个POPUP的窗口专门用来放置窗口,并且该窗口可以用 SetLayeredWindowAttributes 将该POPUP窗口也透明一些,然后这两个窗口同时移动即可(这可能要用到DeferWindowPos来同步移动窗口)。
2. 采用DirectUIHWND的方法,由自己来绘制所有的控件,这个工作量很大,但效果是最好的。但是对于一些特殊控件,如IE Webbrowser,则只能采用第1种方法了;Windowless RichEidt如果在分层窗口中绘制的话,TxDraw函数的HDC来源也是一个比较麻烦的事:用GDI的HDC,则没有Alpha channel,还需要在绘制完之后去填充一下Alpha通道(Win7下可以考虑下BufferedPaintSetAlpha,还没试过),用Gdiplus的GetHDC函数,则效率太低,不能满足。
UpdateLayeredWindow函数使用介绍:
SetLayeredWindowAttributes与UpdateLayeredWindow的区别:
前者只能将整个对话框透明(包括上面的子窗口与控件),而后者的强大之处在于可以自定义每一个像素点的alpha值,实现任意效果.
UpdateLayeredWindow 函数的使用其实有点类似于 SetWindowPos,没用过的时候看起来很复杂。UpdateLayeredWindow 可以设置分层窗口的坐标和大小(想想SetWindowPos的作用,利用这一点我们可以实现完全无闪烁的窗口拉伸。不知道为什么QQ没有实现这一点)。
该函数的参数分析如下:
BOOL UpdateLayeredWindow(
HWND hwnd, // 窗口句柄 HDC hdcDst, // 取 NULL 即可 POINT *pptDst, // 分层窗口的新位置。不改变位置时可设置为NULL.建议保存为一个成员变量 SIZE *psize, // 分层窗口的新大小。不改变位置时可设置为NULL.建议保存为一个成员变量 HDC hdcSrc, // 内存HDC句柄,该HDC的HBITMAP中保存了分层窗口的内容。 POINT *pptSrc, // 设置为POINT pt = {0,0}; 即可 COLORREF crKey, // 一种使用关键色来实现透明的方法。一般都使用alpha来实现,这里设置0即可 BLENDFUNCTION *pblend, // 混合参数 DWORD dwFlags); // 取ULW_ALPHA。
这里面最关键的就是HDC hdcSrc了。
HDC可以用CreateCompatibalDC(NULL)来创建,但选进HDC中的HBITMAP则必须创建为32位色,使其能够带alpha channel。例如:
CImage image;
image.Create( rc.Width(), -rc.Height(), 32, CImage::createAlphaChannel );
BLENDFUNCTION结构的设置可以参考如下:
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER ;
bf.AlphaFormat = AC_SRC_ALPHA;
bf.BlendFlags = 0; // Must be zero.
bf.SourceConstantAlpha = 255; // 0~255 透明度,这里的效果类似于SetLayeredWindowAttributes函数。取255即可,因为我们使用hdcSrc中的alpha值。
WS_EX_TRANSPARENT,加上该属性就能实现“鼠标穿透”效果。窗口周围的阴影肯定也是需要加上该属性的。
// 鼠标拖动窗口效果
UINT OnNcHitTest(CPoint point)
{
return HTCAPTION;
}
方法三、DWM
MARGINS m = {-1};
DwmExtendFrameIntoClientArea(m_hWnd, &m);
在win7下面采用DWM就能够让窗口识别alpha channel,例如使用 BLACK_BRUSH 去刷新窗口背景,如果窗口有标题栏,则这整个窗口都能看到毛玻璃效果,如果窗口没有标题栏,则整个窗口都是透明的(与分层窗口不同,窗口没有被搂空)。
因此如果使用PNG去绘制窗口背景的话,就能使实现窗口阴影,但同样需要注意:gdi没有alpha通道
那QQ 2012在win7下,能够显示阴影+毛玻璃效果的特性是怎么实现的呢?
要实现毛玻璃效果,得调用另外一个DWM函数:DwmEnableBlurBehindWindow。需要注意的是毛玻璃的范围区域不能包含四周的阴影区域,否则阴影区域也会显示成毛玻璃效果了。例如:
HRGN hRgn = CreateRectRgn(10,10,500,500);
DWM_BLURBEHIND blurbehind = {0};
blurbehind.dwFlags = DWM_BB_ENABLE|DWM_BB_BLURREGION|DWM_BB_TRANSITIONONMAXIMIZED;
blurbehind.fEnable = true;
blurbehind.hRgnBlur = hRgn;
blurbehind.fTransitionOnMaximized = TRUE;
DwmEnableBlurBehindWindow(m_hWnd, &blurbehind);
DeleteObject(hRgn);
PS: DWM虽然能使得窗口按照alpha channel 进行透明,但与分层窗口不同的是,它不处理HITTEST。也就是说即使窗口上面有一块地方是完全透明的,你用鼠标在那点击也会点在窗口上面,而不是透过窗口
效果如下: