隨著Microsoft憑藉Windows在作業系統上取得的巨大成績,Windows使用者介面也日益成為業界標準。統一的介面給廣大使用者對應用軟體的學習與使用帶來了很大方便。但每天都面對同一副面孔,日久天長難免會產生一些厭倦,開發一些“離經叛道”,一改Windows應用程式千篇一律的“標準”介面,一定會給你帶來一種清新的感覺。標準Windows應用程式視窗一般為帶有標題欄的淺灰色矩形外觀,因而“異形”對話方塊/視窗也主要是顏色與外形上動手腳。
1:改變背景顏色
改變對話方塊(視窗)的背景顏色是最簡單的改變Windows應用程式外觀的方法,根據Windows建立與管理機理,一般有兩種方法。一種是處理WM_CTLCOLOR訊息,首先建立所選背景顏色的刷子,然後呼叫SetBkColor()或SetDialogBkColor()以所建立的刷子來繪製視窗或對話方塊的背景。需要重畫視窗或對話(或對話的子控制元件)時,Windows向對話傳送訊息WM_CTLCOLOR,應用程式處理WM_CTLCOLOR訊息並返回一個用來繪畫對話背景的刷子控制程式碼。另外一種是響應Windows的WM_ERASEBKGND訊息,Windows向視窗傳送一個WM_ERASEBKGND訊息通知該視窗擦除背景,可以使用VC++的ClassWizard過載該訊息的預設處理程式來擦除背景(實際是用刷子畫),並返回TRUE以防止Windows擦除視窗。
2.改變視窗外形
通過使用新的SDK函式SetWindowRgn(),可以將繪畫和滑鼠訊息限定在視窗的一個指定的區域,因此實際上是使視窗成為指定的不規則形狀(區域形狀)。“區域”是Windows GDI中一種強有力的機制,區域是裝置上的一塊空間,可以是任意形狀,複雜的區域可以由各個小區域組合而成。Windows內含的區域建立函式有CreateRectRgn()、CreatePolyRgn()、CreatePolygonRgn()、CreateRoundRectRgn()和CreateEllipticRgn(),再通過CombineRgn()來組合區域,即可得到複雜形狀的區域,獲得複雜形狀的視窗外形。
通過上面的方法雖然可以得到“異形”視窗,但感覺顏色單調,外形也不夠“COOL”,能否獲得更酷的“異形”對話方塊/視窗呢?回答是肯定的。下面就介紹利用點陣圖和蒙板建立“異形”對話方塊/視窗的方法。
3.利用點陣圖建立異形對話方塊視窗
利用點陣圖建立異形對話方塊原理是根據象素的顏色來進行“扣像”處理,對所有非指定顏色象素區域進行區域組合。利用這一技術,實際上就是實現對話方塊/視窗的點陣圖背景,並且對指定的顏色區域進行透明處理。下面就以透明點陣圖為背景的對話方塊為例來說明:
首先用繪圖軟體如PhotoShop繪製編輯一幅擬做對話方塊背景用的圖片,用BMP格式儲存,假設存為Back.Bmp。需要說明的是,雖然Visual C++整合開發環境的資源編輯器只能編輯不超過16色的點陣圖,但完全我們可以以真彩色方式儲存,不必理會Visual C++的警告。
下一步是用Visual C++的AppWizard建立一個基於對話方塊的應用程式假定命名為Trans。用資源編輯器引入背景圖片Back.Bmp,如果是高彩色,不必理會出現的警告資訊,點選OK確認即可。為了明確,修改預設的資源ID標識IDB_BITMAP1為IDB_BACKBMP。然後修改對話方塊的Style為Popup,Border為None,
向CTransDlg類新增區域處理功能模組void CTransDlg::SetupRegion(CDC *pDC /*對話方塊視窗DC*/, UINT BackBitmapID /*背景點陣圖資源ID*/, UINT MaskBitmapID /*區域處理點陣圖資源ID*/, COLORREF TransColor = 0x00000000 /*透明顏色值,預設為黑色*/)。到目前為止,我們暫時認為MaskBitmapID等同於BackBitmapID。其核心工作是根據MaskBitmapID指示點陣圖的象素顏色進行區域組合。完整的程式碼如下:
void CTransDlg::SetupRegion(CDC *pDC /*對話方塊視窗DC*/,
UINT BackBitmapID /*背景點陣圖資源ID*/,
UINT MaskBitmapID /*區域處理點陣圖資源ID*/,
COLORREF TransColor /*透明顏色值*/)
{
CDC memDC;
CBitmap cBitmap;
CBitmap* pOldMemBmp = NULL;
COLORREF cl;
CRect cRect;
UINT x, y;
CRgn wndRgn, rgnTemp;
//取得視窗大小
GetWindowRect(&cRect);
//背景點陣圖資源ID
m_BackBitmapID = BackBitmapID
//裝載點陣圖
cBitmap.LoadBitmap(MaskBitmapID);
memDC.CreateCompatibleDC(pDC);
pOldMemBmp = memDC.SelectObject(&cBitmap);
//首先建立預設的完整區域為完整的視窗區域
wndRgn.CreateRectRgn(0, 0, cRect.Width(), cRect.Height());
//下面的兩層迴圈為檢查背景點陣圖象素顏色,進行透明區域處理;
//當象素顏色為指定的透明值時,即將該點從區域中剪裁掉。
//其中用到的幾個成員變數m_MaskLeftOff、m_MaskTopOff、
//m_MaskRightOff、m_MaskBottomOff、m_FrameWidth
//和m_CaptionHeight,其作用後面再作說明,此時可全部當作0來處理。
for(x= m_FrameWidth+m_MaskLeftOff;x<=cRect.Width() - m_FrameWidth-m_MaskRightOff; x++){
for(y = m_CaptionHeight+m_MaskTopOff;
y<=cRect.Height() - m_FrameWidth-m_MaskBottomOff; y++){
//取得座標處象素的顏色值
cl = memDC.GetPixel(x - m_FrameWidth-m_MaskLeftOff,y - m_CaptionHeight-m_MaskTopOff);
if(col == TransColor)
{//象素顏色為指定的透明色,建立透明“微區域”
rgnTemp.CreateRectRgn(x, y, x+1, y+1);
//“扣像”,從完整的區域中“扣除”透明的“微區域”
wndRgn.CombineRgn(&wndRgn, &rgnTemp, RGN_XOR);
//刪除剛建立的透明“微區域”,釋放系統資源
rgnTemp.DeleteObject();
}
}
}
if (pOldMemBmp) memDC.SelectObject(pOldMemBmp);
//用設定視窗為指定的區域
SetWindowRgn((HRGN)wndRgn, TRUE);
}
重置系統預設的背景擦除操作,即新增WM_ERASEBKGND訊息處理過程,這一步可以藉助ClassWizard來簡化操作。
BOOL CTransDlg::OnEraseBkgnd(CDC* pDC)
{// TODO: Add your message handler code here and/or call default
CRect rect;
CDC memDC;
CBitmap cBitmap;
CBitmap* pOldMemBmp = NULL;
GetWindowRect(&rect);
//裝載背景點陣圖
cBitmap.LoadBitmap(m_BackBitmapID);
memDC.CreateCompatibleDC(pDC);
pOldMemBmp = memDC.SelectObject(&cBitmap);
//將背景點陣圖複製到視窗客戶區
pDC->BitBlt(0, 0, rect.Width(), rect.Height(),
&memDC, 0, 0, SRCCOPY);
if (pOldMemBmp) memDC.SelectObject( pOldMemBmp );
//刪除系統卻省的OnEraseBkgnd功能