如何实现窗口阴影效果 (SysShadow、分层窗口、DWM)

来源:互联网 发布:anaconda linux 编辑:程序博客网 时间:2024/04/25 05:00

方法一、系统属性:

 

为了实现阴影效果,这两天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。也就是说即使窗口上面有一块地方是完全透明的,你用鼠标在那点击也会点在窗口上面,而不是透过窗口

 

效果如下:


原创粉丝点击