从Windows Vista开始,Aero Glass效果被应用在了Home Premium以上的系统中(Home Basic不具有该效果)。这种效果是由DWM(Desktop Window Manager)来控制的。对于一般的程序,缺省将在窗口边框应用这种效果。但如果我们想要更多的控制,比如让客户区的一部分也呈现这种效果,那也非常的简单。不需要我们在程序里做任何复杂的算法,我们只需要调API,交给DWM去做就可以了。

一、Composition(窗口合成) and Non-client Rendering(非客户区渲染)

非客户区通常包括窗口标题栏和窗口边框。缺省状态下,非客户区会被渲染成毛玻璃效果,这也称为Compostion。有几个函数可以控制系统和当前窗口的渲染方式。同时也有Windows消息用于接受渲染模式的改变。

1.检测系统是否开启Aero Glass。使用 函数 DwmIsCompositionEnabled 检测系统当前是否开启了Aero Glass特效。它接受一个BOOL参数,并将当前状态存储到其中。函数原型:HRESULT DwmIsCompositionEnabled(BOOL *pfEnabled );

2.开启/关闭Aero Glass。使用函数DwmEnableComposition 开启或关闭系统Aero Glass效果,传入DWM_EC_ENABLECOMPOSITION 开启,传入DWM_EC_DISABLECOMPOSITION 关闭。

3.开启/关闭当前窗口的非客户区渲染。函数DwmSetWindowAttribute 用于设置窗口属性,属性DWMWA_NCRENDERING_POLICY 控制当前窗口是否使用非客户区渲染。DWMNCRP_ENABLED 开启,DWMNCRP_DISABLED 关闭。当系统的Aero Glass关闭时,设置无效。与之对应,使用函数DwmGetWindowAttribute 可以检测当前窗口属性。

4.响应系统Aero Glass的开启或关闭。当Aero Glass被开启或关闭时,Windows会发送消息WM_DWMCOMPOSITIONCHANGED , 使用 函数 DwmIsCompositionEnabled 检测状态。

5.响应窗口非客户区渲染的开启或关闭。当前窗口的非客户区渲染开启或关闭时,Windows会发送消息WM_DWMNCRENDERINGCHANGED ,wParam 指示当前状态。

二、Transition(窗口动画) and ColorizationColor(主题颜色)

Transition控制是否以动画方式显示窗口的最小化和还原。通过使用函数DwmSetWindowAttribute ,设置属性DWMWA_TRANSITIONS_FORCEDISABLED ,开启或关闭窗口动画。该设置只对当前窗口有效。

当用户通过控制面板修改主题颜色时,Windows将发送消息WM_DWMCOLORIZATIONCOLORCHANGED ,程序中通过函数DwmGetColorizationColor 取得当前主题颜色,以及是否透明。通过响应颜色的变更,可以让程序的颜色风格随主题风格而变化。

三、开启客户区域Aero Glass效果

函数DwmEnableBlurBehindWindow 开启客户区的Aero Glass效果,第一个参数为窗口句柄,第二个参数为一个DWM_BLURBEHIND 结构。其中fEnable 设置是否开启客户区Glass效果。hRgnBlur 设置Glass效果的区域,该项设置为NULL将使整个客户区呈现Glass效果,设置为一个正确的区域后,该区域将呈现Glass效果, 而区域以外为完全透明。要呈现透明效果需要客户区原始的颜色为黑色,可以在WM_PAINT 消息中绘制客户区,下面的代码使用GDI+,在Aero Glass开启时将整个窗口绘制为黑色,Aero Glass关闭时绘制为灰色:

  1. case WM_PAINT:
  2. {
  3. PAINTSTRUCT ps;
  4. HDC hDC = BeginPaint(hWnd, &ps);
  5. //不要直接使用窗口句柄创建Graphics,会导致闪烁
  6. Graphics graph(hDC);
  7. //清除客户区域
  8. RECT rcClient;
  9. GetClientRect(hWnd, &rcClient);
  10. BOOL bCompEnabled;
  11. DwmIsCompositionEnabled(&bCompEnabled);
  12. SolidBrush br(bCompEnabled? Color::Black : Color::DarkGray);
  13. graph.FillRectangle(&br, Rect(rcClient.left, rcClient.top,
  14. rcClient.right, rcClient.bottom));
  15. EndPaint(hWnd, &ps);
  16. }
  17. break;

GDI+的初始化和关闭仍然是必须的:

  1. //初始化GDI+
  2. ULONG_PTR token;
  3. GdiplusStartupInput input;
  4. GdiplusStartup(&token, &input, NULL);
  5. //*********************************
  6. //关闭GDI+
  7. GdiplusShutdown(token);

下面代码将整个客户区设置为Glass效果:

  1. DWM_BLURBEHIND bb = {};
  2. bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
  3. bb.fEnable = true;
  4. bb.hRgnBlur = NULL;
  5. DwmEnableBlurBehindWindow(hWnd, &bb);

下面代码将客户区中心一个椭圆的区域设置为Glass效果:

  1. RECT rect;
  2. GetWindowRect(hWnd, &rect);
  3. int width = , height = ;
  4. //居中椭圆形
  5. HRGN hRgn = CreateEllipticRgn((rect.right - rect.left)/ - width/,
  6. (rect.bottom - rect.top)/ - height/, (rect.right - rect.left)/ + width/,
  7. (rect.bottom - rect.top)/ + height/);
  8. DWM_BLURBEHIND bb = {};
  9. bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
  10. bb.fEnable = true;
  11. bb.hRgnBlur = hRgn;
  12. DwmEnableBlurBehindWindow(hWnd, &bb);

四、窗口边框向客户区扩展

上面的方式中,非客户区和客户区之间仍然有界限。如何增大Glass效果的范围,并且消除界限呢?那就是使窗口边框向客户区扩展,利用函数DwmExtendFrameIntoClientArea 实现。函数接受一个窗口句柄和一个MARGINS 类型的参数。MARGINS指定了在上下左右4个方向上扩展的范围。如果4个值均为-1,则扩展到整个客户区。

  1. MARGINS margins = {, , , };
  2. DwmExtendFrameIntoClientArea(hWnd, &margins);

  1. MARGINS margins2 = {-}; //将扩展到整个客户区
  2. DwmExtendFrameIntoClientArea(hWnd, &margins2);

五、在窗口上绘制图形

PNG图片带有alpha通道,可以与Aero Glass很好的配合。利用GDI+显示PNG图片非常方便,下面的代码将一张PNG图片加载到内存中:

  1. Bitmap bmp = Bitmap::FromFile(L"Ferrari.png", false);

在WM_PAINT消息处理中,将整个客户区绘制为黑色以后,利用GDI+将图片绘制到窗口客户区:

  1. //绘制图形
  2. int width = bmp->GetWidth();
  3. int height = bmp->GetHeight();
  4. Rect rc(, , width, height);
  5. graph.DrawImage(bmp, rc, , , width, height, UnitPixel);

六、文本的绘制

当窗口大范围的透明之后,窗口上的文字的阅读成了一个问题。Windows的解决办法是为文字加上发光效果(Glowing),标题栏的文本使用的就是这种方式。我们在自己的程序中可以使用DrawThemeTextEx 函数来绘制发光的文字。该函数的原型定义如下:

  1. HRESULT DrawThemeTextEx( HTHEME hTheme,
  2. HDC hdc,
  3. int iPartId,
  4. int iStateId,
  5. LPCWSTR pszText,
  6. int iCharCount,
  7. DWORD dwFlags,
  8. LPRECT pRect,
  9. const DTTOPTS *pOptions
  10. );

hTheme是一个主题句柄,可以使用OpenThemeData 获得, OpenThemeData 函数接受一个窗口句柄,和主题类的名称。iPartId和iStateId分别代表主题类中的Part和State,所有可用的主题类、Part和state在SDK的帮助文档中可以查看到。pszText是要绘制的文本。iCharCount为文字个数,-1代表绘制全部文本。dwFlags指定文本格式。pRect为文本绘制区域。pOptions中可以设定文本的发光、阴影等效果。HDC是一个设备上下文句柄,为了实现类似于标题栏中文本的发光效果,这里不能使用由BeginPaint 得到的句柄,而是要使用CreateCompatibleDC 创建一个内存中的句柄,并且要创建一张位图,通过内存句柄将文本绘制到位图上。然后再将位图转移到窗口上。下面的函数封装了绘制发光文本的过程:

  1. //绘制发光文字
  2. void DrawGlowingText(HDC hDC, LPWSTR szText, RECT &rcArea,
  3. DWORD dwTextFlags = DT_LEFT | DT_VCENTER | DT_SINGLELINE, int iGlowSize = )
  4. {
  5. //获取主题句柄
  6. HTHEME hThm = OpenThemeData(GetDesktopWindow(), L"TextStyle");
  7. //创建DIB
  8. HDC hMemDC = CreateCompatibleDC(hDC);
  9. BITMAPINFO bmpinfo = {};
  10. bmpinfo.bmiHeader.biSize = sizeof(bmpinfo.bmiHeader);
  11. bmpinfo.bmiHeader.biBitCount = ;
  12. bmpinfo.bmiHeader.biCompression = BI_RGB;
  13. bmpinfo.bmiHeader.biPlanes = ;
  14. bmpinfo.bmiHeader.biWidth = rcArea.right - rcArea.left;
  15. bmpinfo.bmiHeader.biHeight = -(rcArea.bottom - rcArea.top);
  16. HBITMAP hBmp = CreateDIBSection(hMemDC, &bmpinfo, DIB_RGB_COLORS, , NULL, );
  17. if (hBmp == NULL) return;
  18. HGDIOBJ hBmpOld = SelectObject(hMemDC, hBmp);
  19. //绘制选项
  20. DTTOPTS dttopts = {};
  21. dttopts.dwSize = sizeof(DTTOPTS);
  22. dttopts.dwFlags = DTT_GLOWSIZE | DTT_COMPOSITED;
  23. dttopts.iGlowSize = iGlowSize; //发光的范围大小
  24. //绘制文本
  25. RECT rc = {, , rcArea.right - rcArea.left, rcArea.bottom - rcArea.top};
  26. HRESULT hr = DrawThemeTextEx(hThm, hMemDC, TEXT_LABEL, , szText, -, dwTextFlags , &rc, &dttopts);
  27. if(FAILED(hr)) return;
  28. BitBlt(hDC, rcArea.left, rcArea.top, rcArea.right - rcArea.left,
  29. rcArea.bottom - rcArea.top, hMemDC, , , SRCCOPY | CAPTUREBLT);
  30. //Clear
  31. SelectObject(hMemDC, hBmpOld);
  32. DeleteObject(hBmp);
  33. DeleteDC(hMemDC);
  34. CloseThemeData(hThm);
  35. }

在绘制了图形后,加入下面代码绘制一段文本:

  1. //绘制文本
  2. RECT rcText = {, , , };
  3. DrawGlowingText(hDC, L" 一点点中文 and some english", rcText);

因为字体发光的缘故,在文本左侧留下一个空格看起来会舒服一些。效果如下:

七、缩略图关联

DWM API中还有一个功能,即缩略图关联。它允许我们将一个窗口的缩略图显示到自己窗口的客户区。缩略图不同于截图,它是实时更新的。下面的代码将在窗口客户区显示QQ影音播放器的缩略图:

  1. HRESULT hr = S_OK;
  2. HTHUMBNAIL thumbnail = NULL;
  3. HWND hWndSrc = FindWindow(_T("QQPlayer Window"), NULL);
  4. hr = DwmRegisterThumbnail(hWnd, hWndSrc, &thumbnail);
  5. if (SUCCEEDED(hr))
  6. {
  7. RECT rc;
  8. GetClientRect(hWnd, &rc);
  9. DWM_THUMBNAIL_PROPERTIES dskThumbProps;
  10. dskThumbProps.dwFlags = DWM_TNP_RECTDESTINATION | DWM_TNP_VISIBLE | DWM_TNP_OPACITY ;
  11. dskThumbProps.fVisible = TRUE;
  12. dskThumbProps.opacity = ;
  13. dskThumbProps.rcDestination = rc;
  14. hr = DwmUpdateThumbnailProperties(thumbnail,&dskThumbProps);
  15. }

首先通过窗口标题查找到源窗口句柄,然后使用DwmRegisterThumbnail 注册缩略图关联,注册成功后,通过DwmUpdateThumbnailProperties 更新缩略图属性,其中设定了是否可视、透明度以及目标绘制区域。得到下面的效果:

源码下载

原文地址:http://www.cnblogs.com/longle/archive/2011/11/14/2248991.html

【转】MFC 迅雷七窗体特效,使用DWM实现Aero Glass效果的更多相关文章

  1. 窗口玻璃特效,半透明窗口,使用DWM实现Aero Glass效果

    转自:http://blog.csdn.net/ntwilford/article/details/5656633 从Windows Vista开始,Aero Glass效果被应用在了Home Pre ...

  2. API、Win32 SDK、Win32项目、MFC、Windows窗体应用程序的区别

    [原]API.Win32 SDK.Win32项目.MFC.Windows窗体应用程序的区别 首先来看一下每一个术语的定义: API:Application Programming Interface. ...

  3. 《MFC游戏开发》笔记七 游戏特效的实现(一):背景滚动

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9344721 作者:七十一雾央 新浪微博:http:// ...

  4. MFC获得主窗体和父窗体指针

    MFC编程中经常遇到子窗体向父窗体传递參数的情况,这就须要获得父窗体的指针. 例:主对话框CMyMainDlg通过buttonButtonA进入对话框CMyParentDlg.CMyParentDlg ...

  5. wpf利用线程制作初始界面和关闭窗体特效

    1.首先定义初始窗体,和主窗体. 初始窗体(StartWindow) 主窗体(MainWindow): 2.在主窗体界面中,加载初始窗体.注意在线程中操作UI元素需要使用BeginInvoke或者In ...

  6. MFC获取各个窗体(体)之间的指针(对象)

    MFC在非常多的对话框操作中,我们常常要用到在一个对话框中调用还有一个对话框的函数或变量.能够用例如以下方法来解决.    HWND hWnd=::FindWindow(NULL,_T("S ...

  7. MFC无边框窗体不响应任务栏点击问题

    为了提升用户体验,需要隐藏主窗体的边框,使用图片绘制新的标题栏.标题栏绘制之后,发现用户点击任务栏上应用程序的图标,应用程序不会随着点击交替隐藏显示. 分析结果是问题出现窗体风格设置上. 最初为了省事 ...

  8. Delphi使用Windows API函数AnimateWindow实现窗体特效

    {**********************************************************************API函数 AnimateWindow 使用:函数功能:窗 ...

  9. MFC 如何在一个窗体中嵌套在另一个窗体中

    其中的一个方法是讲子窗体设置为非模式对话框,具体操作为 :设置子窗体的border属性为none,style为 child. 在父窗体中需要用create来实现,具体例子如下. 在父窗体的OnInit ...

随机推荐

  1. yield与send实现协程操作

    yield与send实现协程操作 之前我们说过,在函数内部含有yield语句即称为生成器. 下面,我们来看看在函数内部含有yield语句达到的效果.首先,我们来看看以下代码: def foo(): w ...

  2. eclipse引入的第三方jar包放到同一个目录下

    相信大家对这个不陌生吧: 使用eclipse,在JAVA项目中导入第三方jar包,然后看到一长串引入jar包信息,如下图: 看着不美观,也不是非常有必要,能不能像图中JRE System Librar ...

  3. python 中hive 取日期时间的方法

    #!/usr/bin/env python3 import sys import os import time, datetime sys.path.append(os.getenv('HIVE_TA ...

  4. 多主机共享ssh Public/Private Key

    前期服务器比较少,所有代码都放在github的私库中,在自己的github 设置中设置SSH keys就可以拉下相应的库中的代码到本地与服务器了,但是最近服务器多家了几台,每台都生成key加到gith ...

  5. JDK1.5新特性,基础类库篇,调用外部命令类(ProcessBuilder)用法

    一. 背景 ProcessBuilder类是用来创建操作系统进程的.与Runtime.exec相比,它提供了更加方便的方法以创建子进程. 每个ProcessBuilder实例管理着一个进程属性的集合. ...

  6. [hihoCoder] #1158 : 质数相关

    时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 两个数a和 b (a<b)被称为质数相关,是指a × p = b,这里p是一个质数.一个集合S被称为质数相关,是指S中 ...

  7. perl的匿名引用

    perl中列表不能嵌套,只能通过引用的方式构建复杂的结构.引用其实就是c中的指针,只不过perl中对指针,也就是一个地址的声明和取值有自己的一套方法. 1.先复习普通标量的引用方法: [vagrant ...

  8. Generics Variance

    http://research.microsoft.com/pubs/64031/designandimplementationofgenerics.pdf Variance and Generali ...

  9. javascript原型继承---constructor篇

    很多人对constructor的理解是指向对象的构造函数,今天才发现这种理解是有偏差的... 其实, constructor指向的不是实例化实例的构造函数,而是实例化该对象的构造函数的原型的构造函数 ...

  10. sqlalchemy使用

    1.SQLAlchemy的作用 ORM对象关系映射技术 2.SQLAlchemy安装 pip install SQLAlchemy 查看SQLAlchemy版本 3.生成数据库连接 from sqla ...