C#迅雷七表單特效,使用DWM實現Aero Glass效果

來源:互聯網
上載者:User

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

 

view plainprint?
  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+的初始化和關閉仍然是必須的:

 

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

 

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

 

view plainprint?
  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效果:

 

view plainprint?
  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,則擴充到整個客戶區。

 

view plainprint?
  1. MARGINS margins = {50, 50, 50, 50};  
  2. DwmExtendFrameIntoClientArea(hWnd, &margins);  

 

 

view plainprint?
  1. MARGINS margins2 = {-1};    //將擴充到整個客戶區  
  2. DwmExtendFrameIntoClientArea(hWnd, &margins2);  

 

五、在視窗上繪製圖形

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

 

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

 

      在WM_PAINT訊息處理中,將整個客戶區繪製為黑色以後,利用GDI+將圖片繪製到視窗客戶區:

 

view plainprint?
  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 函數來繪製發光的文字。該函數的原型定義如下:

 

view plainprint?
  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 建立一個記憶體中的控制代碼,並且要建立一張位元影像,通過記憶體控制代碼將文本繪製到位元影像上。然後再將位元影像轉移到視窗上。下面的函數封裝了繪製發光文本的過程:

 

view plainprint?
  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. }  

 

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

 

view plainprint?
  1. //繪製文本  
  2. RECT rcText = {10, 10, 300, 40};  
  3. DrawGlowingText(hDC, L"  一點點中文 and some english", rcText);  

 

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

七、縮圖關聯

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

 

view plainprint?
  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 更新縮圖屬性,其中設定了是否可視、透明度以及目標繪製地區。得到下面的效果:

 

原始碼下載 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.