前面拜讀過本站無數高手的許多好文章,受益非淺,首先向各位大俠致敬!今天編程偶有小得,不敢獨享,特拿出來與各位共享,希望對某些朋友有些許幫助。
透明窗體的問題相信大家已經很熟悉了,前面的幾期在線雜志也有幾篇詳盡的教程,總結一下就是通過SetWindowRgn這個函數來實現,具體的裁切框用CRgn來生成,比較簡單的 象圓,橢圓,圓角窗口等CRgn類都提供了相應的生成方法,我們如果想根據自己的圖片來生成裁切框前面的朋友提到的方法是首先生成一個矩形裁切框,然後掃描圖片,根據象素點的顏色與掩碼顏色的匹配與否,對裁切框進行刪減(生成一個新的,然後XOR),對於這種方法我就不詳細描述了,有需要的朋友請查閱以前的文章,我首先說一下我遇到的不足之處:
如果我的窗體支持Resize,那麼我調整大小的過程中,要不停的計算裁切框(要逐點掃描象素,並對裁切框進行操作),計算量相當大,特別當窗體比較大的時候更是如此,會造成窗體的閃爍。
我查閱相關資料得到另一種實現方法,簡單實用,那就是利用 SetLayeredWindowAttributes 這個函數,相信許多朋友都見過Microsoft對他的描述但用過的並不多,要用它,要安裝最新的SDK,否則會出現沒有定義的錯誤。小弟懶得下載,下面的介紹采用了一般API調用的格式。如果你已經有最新的SDK,那你的程序可以變得更加簡練!
首先介紹一下這個函數:
BOOL SetLayeredWindowAttributes(
HWND hwnd, // 應用目標窗口的句柄
COLORREF crKey, // 掩碼的顏色,可以用RGB(r,g,b)來指定
BYTE bAlpha, // 掩碼顏色部分的Alpha值,0是全透明,255是完全不透明
DWORD dwFlags // 透明方式
);
要說名的是這個函數只在Windows2000及以上版本才支持。MSDN對要求的描述如下
<Requirements>
Windows NT/2000/XP: Included in Windows 2000 and later.
Windows 95/98/Me: Unsupported.
Header: Declared in Winuser.h; include Windows.h.
Library: Use User32.lib.
還有就是這個函數對於有標題框的窗體支持不好,就是它裁切的只是客戶區域,好在我們要制作透明窗體的場合一般用不到標題框下面就說名例程的制作過程。(我旨在說明這種透明窗體的思路及函數的用法,所以代碼非常簡單,並且沒有必要的錯誤驗證機制,希望大家諒解)
建立一張用於在窗體上繪制的背景圖片,把要裁切的部分用一種顏色標記出來,我們叫它MaskColor,我的圖片如下:
我的MaskColor = 0xFF00,也就是 RGB(0,255,0).
建立一個基於對話框的工程,修改對話框資源的屬性,主要修改兩個地方。一是指定沒有TitleBar,二是指定BorderStyle為None.這樣才能保證出來的窗體符合你的要求
把圖片加入資源,付ID = IDB_BACKGROUND
下面就開始寫代碼了,呵呵,看下面的代碼這麼長。是不是頭有點大呀,別急,這些多半都是工程向導自動生成的,我加的都已經注解上了,並用黃色表示沒有幾行的。要不然我怎麼敢向各位吹噓這個實現方法簡單呢。
a.首先我們給窗體添加兩個成員變量:CBitmap * m_oldBitmap; //指向內存DC原來的 Bitmap
CDC m_DC; //用於存放背景圖片的內存DC
b.在窗體的OnInitDialog()函數中做一番初始化: BOOL CTransWindowDlg::OnInitDialog()
就像注釋的那樣,我們首先把圖片Load進來,然後把m_DC創建一個與窗口DC兼容的DC,並把剛才Load進來的圖片綁定到該內存DC上,並用m_oldBitmap 記錄下原有Bitmap,用戶最後釋放。
{
CDialog::OnInitDialog();
// 設置此對話框的圖標。當應用程序主窗口不是對話框時,框架將自動
// 執行此操作
SetIcon(m_hIcon, TRUE); // 設置大圖標
SetIcon(m_hIcon, FALSE); // 設置小圖標
///////////////////////////////////////
//段會鋒添加的代碼
//實現背景圖以及窗口透明
//調用背景圖片
CBitmap bitmap;
BITMAP bitInfo;
bitmap.LoadBitmap(IDB_BACKGROUND);
//得到圖片大小並調整窗口大小適應圖片
bitmap.GetBitmap(&bitInfo);
CRect rect;
GetWindowRect(&rect);
rect.right = rect.left + bitInfo.bmWidth;
rect.bottom = rect.top + bitInfo.bmHeight;
MoveWindow(rect);
//創建並保存DC
m_DC.CreateCompatibleDC(GetDC());
m_oldBitmap = m_DC.SelectObject(&bitmap);
//設置窗口掩碼顏色和模式
//首先獲得掩碼顏色
COLORREF maskColor = m_DC.GetPixel(0,0);
#define LWA_COLORKEY 0x00000001
#define WS_EX_LAYERED 0x00080000
typedef BOOL (WINAPI *lpfnSetLayeredWindowAttributes)(HWND hWnd,
COLORREF crKey,
BYTE bAlpha,
DWORD dwFlags);
lpfnSetLayeredWindowAttributes SetLayeredWindowAttributes;
HMODULE hUser32 = GetModuleHandle("user32.dll");
SetLayeredWindowAttributes = (lpfnSetLayeredWindowAttributes)GetProcAddress(hUser32,
"SetLayeredWindowAttributes");
SetWindowLong(GetSafeHwnd(),
GWL_EXSTYLE,
GetWindowLong(GetSafeHwnd(),
GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(GetSafeHwnd(),
maskColor,
255,
LWA_COLORKEY);
FreeLibrary(hUser32);
////////////////////////////////////////
return TRUE; // 除非設置了控件的焦點,否則返回 TRUE
}
c.向OnPaint中添加代碼,用於把背景圖片繪制到窗口上: void CTransWindowDlg::OnPaint()
{
if (IsIconic())
{
//這裡是MFC的框架代碼,為了減少篇幅省略…
}
else
{
////////////////////////////////////
//段會鋒修改的代碼,用於繪制背景圖片
//CDialog::OnPaint();
CDC * pDC = this->GetDC();
CRect rect;
GetWindowRect(&rect);
pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_DC,0,0,SRCCOPY);
////////////////////////////////////
}
}
d.到這裡我們要的功能就已經能夠實現了,但是好的程序員絕對不應該忘記釋放資源,你也一樣,一定沒有忘記在程序結束時釋放資源,呵呵,我們可以寫到析構函數中,我們也可放到OnClose()函數中,都一樣,我采用了後一種:添加函數並添加釋放資源的代碼
void CTransWindowDlg::OnClose()
{
////////////////////////////////////
//段會鋒添加的代碼
//釋放資源
CBitmap * bitmap = m_DC.SelectObject(m_oldBitmap);
m_DC.DeleteDC();
bitmap->DeleteObject();
////////////////////////////////////
CDialog::OnClose();
}
e.現在好了,運行一下吧,真爽,就這麼幾行代碼搞定了一個漂亮的窗口。是不是很有成就感?呵呵。又看了幾次真的美滋滋的。不好發現問題了,怎麼程序運行的時候開始有一下閃爍呢?哦,是清空背景的時候畫了一下,沒關系,讓我們干掉它。添加WM_EraseBkgnd事件的響應函數,把原來的注釋掉直接返回True,再運行一下看看吧?怎麼樣,滿意了嗎? BOOL CTransWindowDlg::OnEraseBkgnd(CDC* pDC)
{
////////////////////////////////////
//段會鋒編輯的代碼
//防止開始繪制的一下閃爍
//return CDialog::OnEraseBkgnd(pDC);
return true;
////////////////////////////////////
}
f.好了,我已經非常滿意了,要休息一下了,但我怎麼關閉這個窗口呢?糟糕,非要我用Alt+F4不成?算了再多用一下功,寫個雙擊事件好了:void CTransWindowDlg::OnLButtonDblClk(UINT nFlags, CPoint point)
{
///////////////////////////////////
//段會鋒添加的代碼,雙擊窗口關閉Windows
this->PostMessage(WM_CLOSE);
///////////////////////////////////
CDialog::OnLButtonDblClk(nFlags, point);
}
呵呵,本文旨在說明透明窗體的思路及函數用法,拋磚引玉,相信大家一定有好多更好的創意通過這個思路實現,比如,可以添加Resize功能呀,使掩碼不完全透明甚至是漸隱呀等等,總之創意無限,等待著大家的發掘!
本文不完整之處是沒有實現拖動,要添加請參閱前面朋友的相關文章,再此對所有把編程經驗和心得拿出來與大家共享的朋友致意最崇高的敬意,並號召大家積極向前面的朋友學習!但願我的努力能夠為你提供點點幫助,那我就無比欣慰了,如有任何問題或疑問請 Email: [email protected],或者QQ:86685028)
本文配套源碼