创建任意形状的窗口

发表于:2007-07-04来源:作者:点击数: 标签:
北京市 崔英善 在Windows 系统中窗口的默认形状是矩形, 在实际应用中绝大多数窗口都是标准矩形窗口。如果一个窗口有着与众不同的形状, 则会非常引人注目, 如何创建非标准窗口呢? 在Windows, 可以通过调用SetWindowRgn ( 在MFC 类库中对应的函数为CWnd::SetWin
北京市 崔英善

  在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 ) ;
  效果如下图所示:
http://www.ltesting.net/uploads/2007/07/1_200707042009144.jpg (8834 字节)
  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 ) ;
  效果如下图所示:
http://www.ltesting.net/uploads/2007/07/1_200707042009145.jpg (5856 字节)
  那么我们能不能创建任意形状的窗口呢? 也就是创建任意形状的区域呢? 答案是肯定的。大家请看下面这个圆号形状的窗口。
http://www.ltesting.net/uploads/2007/07/1_200707042009146.jpg (38260 字节)
  创建这样的一个区域是调用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 ) 即可。  

原文转自:http://www.ltesting.net