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

一、序言 

  在绝大多数的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”的方法,第一种最简单,如果所需的窗体形状是简单的几何图形,这种方法最合适;第二种稍微复杂些,但是创建的窗体形状更多些;第三种方法可以创建任何在图片中画出的窗体形状,但是实现的复杂度也最高。 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Using CVtxButton Implementing the CVtxButton class is surprisingly easy: Include Vtx.h, Vtx.ccp, VtxButton.h, and VtxButton.cpp in your project. Drop a button on your dialog in Developer Studio. Add #include "VtxButton.h" immediately before #include "MyDlg.h" in MyDlg.cpp and MyApp.cpp. For each button on the dialog that you want to be a CVtxButton, add CVtxButton m_cButton1; immediately after //{{AFX_DATA(CMyDlg) in the public section of your dialog class' specification. Also add DDX_Control(pDX, IDC_BUTTON1, m_cButton1); immediately after //{{AFX_DATA_MAP(CMyDlg) in DoDataExchange() for each button. This is enough to create a default CVtxButton. The button is drawn as a rectangle that takes up the entire client area. The sides will not be shaded the same as CButton because they are colored according to the difference of their angle and the angle of light source. If you use at least one CVtxButton on a dialog, it is suggested that you change all your CButton's to CVtxButton's to maintain a coordinated look. If you want to change the look of the CVtxBuUtton, there are two ways of doing so: Use a predefined shape by adding m_cButton1.SetVtx(VTX_RECT); to OnInitDialog() in MyDlg.cpp. There are four predefined shapes which can be passed as an argument to SetVtx(): VTX_RECT VTX_DIAMOND VTX_CIRCLE VTX_STRETCHEDCIRCLE Create a CVtxPolygons object and pass it as an argument to SetVtx(): CRect rect; m_cButton1.GetClientRect(&rect); // Get the button's original dimensions CVtxPolygons vtxPolygons; int offset[4] = {0, 1, 2, 4}; for (int i = 0; i < 4; i++) // Iterate through each of the polygons { // Add the corners vtxPolygons.Add(i, CVertex(rect.left + offset[i], rect.top + offset[i])); vtxPolygons.Add(i, CVertex(rect.right - offset[i] - 1, rect.top + offset[i])); vtxPolygons.Add(i, CVertex(rect.right - offset[i] - 1, rect.bottom - offset[i] - 1)); vtxPolygons.Add(i, CVertex(rect.left + offset[i], rect.bottom - offset[i]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值