积沙成塔之VC不规则按钮的创建

来源:互联网 发布:mac xshell下载 编辑:程序博客网 时间:2024/05/25 19:56
一、序言 

  在绝大多数的Windows应用程序中,其窗体都是使用的正规正矩的矩形窗体,例如我们常用的,“记事本”,“扫雷”,等等。矩形窗体,具有编程实现简单,风格简洁的优点,所以在普通文档应用程序和简单小游戏中使用足矣。但在某些娱乐游戏程序中使用就略显呆板些了,这时若用不规则窗体替代原先的矩形窗体,将会使这类程序更添情趣。典型的例子有windows   自代的Media   Player,新版本的Media   Player有个控制面板的选项,选中这些面板,播放器就以选中的面板形状出现,这时的播放器比以前版本的Media   Player的古老矩形界面要生动有趣的多了。   要实现不规则窗体不是太难,知道了基本原理后,你也可以创建各种有趣的不规则窗体。 

二、实现原理 

  所有的   Windows   窗体都位于一个称为“region”中,窗体的大小如果超出“region”的范围,windows   会自动裁剪超出 "region "范围那部分的窗体,使其不可见。所以,要创建不规则窗体有两个步骤:第一步就是创建不规则 "region ".第二步就是将窗体放到创建的“region”中。 
  其中第二步很简单就调用一条语句即可。在SDK中调用API函数SetWindowRgn,该函数原型如下:int   SetWindowRgn(   HWND   hWnd,   HRGN   hRgn,   BOOL   bRedraw   ); 
  其中hWnd为待设置的窗体句柄,hRgn为已经创建的 "region "句柄,bRedraw代表是否要重绘窗体。在MFC   中使用窗口类CWnd的成员函数int   CWnd::SetWindowRgn(HRGN   hRgn,   BOOL   bRedraw   );该函数的参数意义与API中同名函数相同。 
  相对与第二步,创建不规则窗体的第一步要复杂许多,并且不规则窗体越复杂,创建其 "region "的过程也越复杂。接下去我们将由浅入深地介绍各种创建”region”的方法。 
  在MFC中 "region "对象,由CRgn类实现。CRgn的几乎每个成员函数都有同名的SDK   API函数对应。 

三、简单“region”的创建 

  类CRgn创建一个新的 "region "的简单方法有以下几个成员函数: 

BOOL   CRgn::CreateRectRgn(   int   x1,   int   y1,   int   x2,   int   y2   );   创建矩形的“region”。   
BOOL   CRgn::CreateEllipticRgn(   int   x1,   int   y1,   int   x2,   int   y2   );   创建圆形或椭圆形“region”。   
BOOL   CRgn::CreateRoundRectRgn(   int   x1,   int   y1,   int   x2,   int   y2,   int   x3,   int   y3   );   创建圆角矩形“region”。   
BOOL   CRgn::CreatePolygonRgn(   LPPOINT   lpPoints,   int   nCount,   int   nMode   );   创建多边形“region”。   
  这里以创建椭圆窗体为例,介绍椭圆窗体创建的方法。在创建椭圆“region”的CreateEllipticRgn函数中,x1,y1指椭圆所在矩形的左上角坐标,x2,y2指该矩形的右下角坐标。 
  下面的代码加入到MFC对话框程序的OnInitDialog函数中,可将该对话框变成椭圆窗体: 


BOOL   CTestDlg::OnInitDialog() 

CDialog::OnInitDialog(); 
... 
CRgn   rgn; 
rgn.   CreateEllipticRgn(0,0,200,100); 
SetWindowRgn(rgn,TRUE); 



图一   椭圆窗体效果图 

四、作图路径法创建”region” 

使用该方法创建”region”的过程如下: 
第一步绘制所要创建的窗体形状。 
该步骤中使用到CDC类中的一些成员函数如下:BOOL   CDC::BeginPath(   ); 
调用该函数后当前设备环境(DC)开始追踪绘图的过程。 

int   CDC::SetBkMode(   int   nBkMode   ); 
设置绘图时的背景模式,此应用中nBkMode必须取值为TRANSPARENT   。即设置绘图时背景不发生变化。 

BOOL   CDC::EndPath(   ); 
调用该函数后当前设备环境(DC)结束追踪绘图的过程。 
  开始绘图前,先调用BeginPath,然后调用SetBkMode。接下去就可调用CDC的其他绘图函数作图,例如Arc,AngleArc,LineTo,MoveTo,RoundRect,,Textout等等。绘图完毕调用EndPath(). 

第二步将绘制的结果转成”region”. 

此步骤中使用SDK   API函数 

HRGN   PathToRegion(   HDC   hdc   ); 
  Hdc为作图DC的句柄,   CDC类中的m_hDC成员变量可做此参数传入。示例,将下面代码加入某个按钮单击事件中,可以将当前窗体变为字符串”hello”的形状 


void   CTestDlg::OnTest()   

        HRGN   wndRgn; 
        CClientDC   dc(this); 
        CFont   mFont; 
        
        if   (dc.m_hDC!=NULL) 
        { 
                VERIFY(mFont.CreateFont( 
                        200,   50,   0,   0,   FW_HEAVY,   TRUE,   FALSE,   
                        0,   ANSI_CHARSET,   OUT_DEFAULT_PRECIS,                 
                        CLIP_DEFAULT_PRECIS,   DEFAULT_QUALITY,                           
                        DEFAULT_PITCH   |   FF_SWISS,   "宋体 "));                                    
        
//开始记录窗体轮廓路径 
                dc.BeginPath();

                //设置背景为透明模式,这句话是必须有的。 
                dc.SetBkMode(TRANSPARENT);
                
                CFont   *   pOldFont; 
                pOldFont   =   dc.SelectObject(   &mFont   ); 
                dc.TextOut(0,   0,   "Hello "); 

                //结束记录窗体轮廓路径 
                dc.SelectObject(   pOldFont   ); 
                dc.EndPath(); 
                
                //把所记录的路径转化为窗体轮廓句柄 
                wndRgn   =   ::PathToRegion(dc.m_hDC); 

                //赋予窗体指定的轮廓形状 
                this-> SetWindowRgn(wndRgn,   TRUE);
        } 


CClientDC是CDC的派生类,故此该类具有所有CDC类的成员变量和成员函数。   


图二   hello形状的窗体效果图 

五、根据图像创建”region” 

  此法创建不规则窗体比较复杂。首先准备一张含有目标窗体形状的图片,设置透明色即将图片中部不属于窗体形状的部分,标记成同一种颜色,例如蓝色RGB(0,0,255).程序运行后先装入图片。然后逐个扫描图片的每个像素,如这个像素不属于透明色,则在相应位置创建一个只含一个像素的“region”然后将这些小”region   ”合并起来组成一个任意形状的”region”.这里将使用到CRgn的一个成员函数   :int   CRgn::CombineRgn(   CRgn*   pRgn1,   CRgn*   pRgn2,   int   nCombineMode   ); 
  其中pRgn1,pRgn2为要合并的两个“region”,nCombineMode为合并的方式,此应用中取RGN_OR,即两”region”全部合并去处重复部分。代码实现如下: 


void   SetupRegion( 
CDC   *pDC,   //窗体的DC指针 
CBitmap   &cBitmap,   //含有窗体形状的位图对象 
COLORREF   TransColor   //透明色 

{         CDC   memDC; 
        //创建与传入DC兼容的临时DC 
        memDC.CreateCompatibleDC(pDC); 

        CBitmap   *pOldMemBmp=NULL; 
        //将位图选入临时DC 
        pOldMemBmp=memDC.SelectObject(&cBitmap); 
      
        CRgn   wndRgn; 
        //创建总的窗体区域,初始region为0 
        wndRgn.CreateRectRgn(0,0,0,0); 
  
        BITMAP   bit;       
        cBitmap.GetBitmap   (&bit);//取得位图参数,这里要用到位图的长和宽           
      
        int   y; 
                for(y=0;y <=bit.bmHeight     ;y++) 
                { 
        CRgn   rgnTemp;   //保存临时region 
                        
                        int   iX   =   0; 
                        do 
                        { 
                                //跳过透明色找到下一个非透明色的点. 
                                while   (iX   <=   bit.bmWidth     &&   memDC.GetPixel(iX,   y)   ==   TransColor) 
                                        iX++; 

                                //记住这个起始点 
                                int   iLeftX   =   iX; 

                                //寻找下个透明色的点 
                                while   (iX   <=   bit.bmWidth     &&   memDC.GetPixel(iX,   y)   !=   TransColor) 
                                        ++iX; 

                                //创建一个包含起点与重点间高为1像素的临时“region” 
                                rgnTemp.CreateRectRgn(iLeftX,   y,   iX,   y+1); 

                                //合并到主 "region ". 
                                wndRgn.CombineRgn(&wndRgn,   &rgnTemp,   RGN_OR); 
                                
//删除临时 "region ",否则下次创建时和出错 
                                rgnTemp.DeleteObject(); 
                        }while(iX   GetWindow(); 
        pWnd-> SetWindowRgn(wndRgn,TRUE);         
        pWnd-> SetForegroundWindow();         


  上述代码创建的不规则窗体中,在OnEraseBkgnd事件中绘制该位图,就可得到与该位图形状一模一样的窗体。  


图三   根据位图和位图中的透明色创建的窗体效果图 

六、小结 

  三种创建“region”的方法,第一种最简单,如果所需的窗体形状是简单的几何图形,这种方法最合适;第二种稍微复杂些,但是创建的窗体形状更多些;第三种方法可以创建任何在图片中画出的窗体形状,但是实现的复杂度也最高。