在Windows
系統中窗口的默認形狀是矩形,
在實際應用中絕大多數窗口都是標準矩形窗口。如果一個窗口有著與眾不同的形狀,
則會非常引人注目, 如何創建非標準窗口呢?
在Windows, 可以通過調用SetWindowRgn ( 在MFC 類庫中對應的函數為CWnd::SetWindowRgn)
來設置窗口的形狀, 函數的定義如下:
int SetWindowRgn( HWND hWnd, HRGN hRgn, BOOL bRedrawflag);
窗口的形狀由參數hRgn 所標志的的區域(region)
決定。通過創建不同的區域就可以創建不同形狀的窗口。下面的代碼,
可以產生圓角矩形的窗口。
CRgn trgn ;
trgn.CreateRoundRectRgn( 0 , 0 , 200 , 200 , 30 , 30 ) ;
SetWindowRgn( trgn , TRUE ) ;
效果如下圖所示:
Windows 還支持由路徑( Path ) 創建區域( Region ),
通過這個途徑我們還可以創建文字形狀的窗口。
下面的代碼可以創建形狀為“計算機世界" 五個字的窗口。
+CDC *pDC = GetDC() ;
CFont rfont , *pOldFont ;
CRgn m_rgn ;
rfont.CreatePointFont( 1000 , "隸書" ) ;
pOldFont = pDC ->SelectObject( &rfont ) ;
pDC ->BeginPath() ;
pDC ->SetBkMode( TRANSPARENT ) ;
CString stxt = "計算機世界" ;
pDC ->TextOut( 0 , 0 , stxt ) ;
pDC ->EndPath() ;
m_rgn.CreateFromPath( pDC ) ;
pDC ->SelectObject( pOldFont ) ;
ReleaseDC( pDC ) ;
SetWindowRgn( m_rgn , TRUE ) ;
效果如下圖所示:
那么我們能不能創建任意形狀的窗口呢?
也就是創建任意形狀的區域呢?
答案是肯定的。大家請看下面這個圓號形狀的窗口。
創建這樣的一個區域是調用SDK 的ExtCreateRegion 來實現( MFC
中的對應函數為CRgn::CreateFromData)。這個函數是通過提供一個矩形數組,
來創建一個由這些矩形組成的區域。而創建圓號區域的過程就是由一個圓號的位圖生成矩形數組,
再由這個矩形數組生成區域的過程。
下面是由圓號位圖創建區域的函數代碼。
CDib m_dib ;
CRgn m_rgn ;
COLORREF m_dwColorKey = 0x0000ff; // 透明色, 純藍
BOOL CAswDlg::CreateRegionFromBmp( LPCSTR lpsFName )
{
// 讀入位圖
if( !m_dib.Open( lpsFName ) )
return FALSE ;
SIZE dibsize ;
// 獲取位圖尺寸
dibsize = m_dib.GetSize( ) ;
int i , j ;
BOOL bkey ;
int iCount = 0 ;
// 統計需要的矩形個數
for( i = 0 ; i < dibsize.cy ; i ++)
{
bkey = TRUE ;
for( j = 0 ; j < dibsize.cx ; j ++)
{
if( m_dib.GetPixel( j , i ) == m_dwColorKey )
{
bkey = TRUE ;
}
else
{
if( bkey )
{
iCount ++;
}
bkey = FALSE ;
}
}
}
BYTE *pData ;
RGNDATA *pRgnData ;
RECT *pRect ;
int iIndex = 0 ;
pData = new BYTE[ sizeof
( RGNDATAHEADER ) +sizeof( RECT ) *iCount ] ;
pRgnData = ( RGNDATA *)pData ;
pRect = ( RECT *)( pData +sizeof( RGNDATAHEADER ) ) ;
pRgnData ->rdh.dwSize =sizeo(RGNDATAHEADER ) ;
pRgnData ->rdh.iType =RDH_RECTANGLES ;
pRgnData ->rdh.nCount = iCount ;
pRgnData ->rdh.nRgnSize = sizeof( RECT ) *iCount ;
pRgnData ->rdh.rcBound.left = 0 ;
pRgnData ->rdh.rcBound.top = 0 ;
pRgnData ->rdh.rcBound.right =dibsize.cx ;
pRgnData ->rdh.rcBound.bottom = dibsize.cy ;
int iLeft = 0 ;
for( i = dibsize.cy -1 ; i >= 0 ; i --)
// 因為Bitmap 位圖在Y 方向是顛倒
的所以要從底部開始
{
bkey = TRUE ;
iLeft = -1 ;
for( j = 0 ; j < dibsize.cx ; j ++)
{
if( m_dib.GetPixel( j , i ) == m_dwColorKey )
{
if( !bkey )
{
pRect[ iIndex ].left = iLeft ;
pRect[ iIndex ].right = j ;
pRect[ iIndex ].top = dibsize.cy -i -1 ;
pRect[iIndex].bottom = dibsize.cy -i;
bkey = TRUE ;
iIndex ++;
iLeft = -1 ;
}
}
else
{
if( bkey )
{
iLeft = j ;
bkey = FALSE ;
}
}
}
if( iLeft >= 0 )
{
pRect[ iIndex ].left = iLeft ;
pRect[ iIndex ].right = dibsize.cx ;
pRect[ iIndex ].top = i ;
pRect[ iIndex ].bottom = i ;
iIndex ++;
}
}
BOOL br = m_rgn.CreateFromData
( NULL , sizeof( RGNDATAHEADER ) +sizeof
( RECT ) *iCount , pRgnData ) ;
return br ;
}
有了這個方法, 任意形狀的窗口都可以被創建,
只要先畫出想要的形狀位圖即可。
這里還要談到一個問題, 任意形狀的窗口沒有標題欄,
那么用戶如何拖動窗口呢? 其實只要在響應左鍵點擊消息時調用SendMessage(
WM_SYSCOMMAND , SC_MOVE | HTCLIENT , 0 ) 即可。