在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 ) 即可。