窗口玻璃特效,半透明窗口,使用DWM實現Aero Glass效果


轉自:http://blog.csdn.net/ntwilford/article/details/5656633

從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關閉時繪制為灰色:

 

[cpp]  view plain copy
  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+的初始化和關閉仍然是必須的:

 

[cpp]  view plain copy
  1. //初始化GDI+  
  2. ULONG_PTR token;  
  3. GdiplusStartupInput input;  
  4. GdiplusStartup(&token, &input, NULL);  
  5. //*********************************  
  6. //關閉GDI+  
  7. GdiplusShutdown(token);  

 

       下面代碼將整個客戶區設置為Glass效果:

 

[cpp]  view plain copy
  1. DWM_BLURBEHIND bb = {0};  
  2. bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;  
  3. bb.fEnable = true;  
  4. bb.hRgnBlur = NULL;  
  5. DwmEnableBlurBehindWindow(hWnd, &bb);  

 

      下面代碼將客戶區中心一個橢圓的區域設置為Glass效果:

 

[cpp]  view plain copy
  1. RECT rect;  
  2. GetWindowRect(hWnd, &rect);  
  3. int width = 300, height = 200;  
  4. //居中橢圓形  
  5. HRGN hRgn = CreateEllipticRgn((rect.right - rect.left)/2 - width/2,   
  6.     (rect.bottom - rect.top)/2 - height/2, (rect.right - rect.left)/2 + width/2,   
  7.     (rect.bottom - rect.top)/2 + height/2);  
  8. DWM_BLURBEHIND bb = {0};  
  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,則擴展到整個客戶區。

 

[cpp]  view plain copy
  1. MARGINS margins = {50, 50, 50, 50};  
  2. DwmExtendFrameIntoClientArea(hWnd, &margins);  

 

 

[cpp]  view plain copy
  1. MARGINS margins2 = {-1};    //將擴展到整個客戶區  
  2. DwmExtendFrameIntoClientArea(hWnd, &margins2);  

 

五、在窗口上繪制圖形

      PNG圖片帶有alpha通道,可以與Aero Glass很好的配合。利用GDI+顯示PNG圖片非常方便,下面的代碼將一張PNG圖片加載到內存中:

 

[cpp]  view plain copy
  1. Bitmap bmp  = Bitmap::FromFile(L"Ferrari.png"false);  

 

      在WM_PAINT消息處理中,將整個客戶區繪制為黑色以后,利用GDI+將圖片繪制到窗口客戶區:

 

[cpp]  view plain copy
  1. //繪制圖形  
  2. int width = bmp->GetWidth();  
  3. int height = bmp->GetHeight();  
  4. Rect rc(30, 30, width, height);  
  5. graph.DrawImage(bmp, rc, 0, 0, width, height, UnitPixel);  

 

六、文本的繪制

      當窗口大范圍的透明之后,窗口上的文字的閱讀成了一個問題。Windows的解決辦法是為文字加上發光效果(Glowing),標題欄的文本使用的就是這種方式。我們在自己的程序中可以使用DrawThemeTextEx 函數來繪制發光的文字。該函數的原型定義如下:

 

[cpp]  view plain copy
  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 創建一個內存中的句柄,並且要創建一張位圖,通過內存句柄將文本繪制到位圖上。然后再將位圖轉移到窗口上。下面的函數封裝了繪制發光文本的過程:

 

[cpp]  view plain copy
  1. //繪制發光文字  
  2. void DrawGlowingText(HDC hDC, LPWSTR szText, RECT &rcArea,   
  3.     DWORD dwTextFlags = DT_LEFT | DT_VCENTER | DT_SINGLELINE, int iGlowSize = 10)  
  4. {  
  5.     //獲取主題句柄  
  6.     HTHEME hThm = OpenThemeData(GetDesktopWindow(), L"TextStyle");  
  7.     //創建DIB  
  8.     HDC hMemDC = CreateCompatibleDC(hDC);  
  9.     BITMAPINFO bmpinfo = {0};  
  10.     bmpinfo.bmiHeader.biSize = sizeof(bmpinfo.bmiHeader);  
  11.     bmpinfo.bmiHeader.biBitCount = 32;  
  12.     bmpinfo.bmiHeader.biCompression = BI_RGB;  
  13.     bmpinfo.bmiHeader.biPlanes = 1;  
  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, 0, NULL, 0);  
  17.     if (hBmp == NULL) return;  
  18.     HGDIOBJ hBmpOld = SelectObject(hMemDC, hBmp);  
  19.     //繪制選項  
  20.     DTTOPTS dttopts = {0};  
  21.     dttopts.dwSize = sizeof(DTTOPTS);  
  22.     dttopts.dwFlags = DTT_GLOWSIZE | DTT_COMPOSITED;  
  23.     dttopts.iGlowSize = iGlowSize;  //發光的范圍大小  
  24.     //繪制文本  
  25.     RECT rc = {0, 0, rcArea.right - rcArea.left, rcArea.bottom - rcArea.top};  
  26.     HRESULT hr = DrawThemeTextEx(hThm, hMemDC, TEXT_LABEL, 0, szText, -1, 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, 0, 0, SRCCOPY | CAPTUREBLT);  
  30.     //Clear  
  31.     SelectObject(hMemDC, hBmpOld);  
  32.     DeleteObject(hBmp);  
  33.     DeleteDC(hMemDC);  
  34.     CloseThemeData(hThm);  
  35. }  

 

      在繪制了圖形后,加入下面代碼繪制一段文本:

 

[cpp]  view plain copy
  1. //繪制文本  
  2. RECT rcText = {10, 10, 300, 40};  
  3. DrawGlowingText(hDC, L"  一點點中文 and some english", rcText);  

 

     因為字體發光的緣故,在文本左側留下一個空格看起來會舒服一些。效果如下:

七、縮略圖關聯

       DWM API中還有一個功能,即縮略圖關聯。它允許我們將一個窗口的縮略圖顯示到自己窗口的客戶區。縮略圖不同於截圖,它是實時更新的。下面的代碼將在窗口客戶區顯示QQ影音播放器的縮略圖:

 

[cpp]  view plain copy
  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 = 200;  
  13.     dskThumbProps.rcDestination = rc;  
  14.     hr = DwmUpdateThumbnailProperties(thumbnail,&dskThumbProps);  
  15. }  

 

       首先通過窗口標題查找到源窗口句柄,然后使用DwmRegisterThumbnail 注冊縮略圖關聯,注冊成功后,通過DwmUpdateThumbnailProperties 更新縮略圖屬性,其中設定了是否可視、透明度以及目標繪制區域。得到下面的效果:

 

 

項目圖,轉.rar


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM