多语言展示
当前在线:1996今日阅读:27今日分享:41

MFC中异形分层窗口设计

关于异形窗口,网上有很多关于这方面的教程,杂七杂八,有很多的问题,比如异形窗口控件不显示等等,关于复杂异形窗口,可以通过分层窗口来解决。下面说一下设计思路。
方法/步骤
1

首先,准备两个窗口,窗口A和窗口B,窗口A作为显示窗口,也就是异形窗口,而窗口B作为逻辑窗口,就是你要处理使用的窗口,即主窗口,然后让这两个窗口重叠在一块,也可以说是在窗口B上创建了窗口A,然后通过UpdateLayeredWindow对窗口A实现异形,因为窗口A在窗口B上,那么势必会遮盖住窗口B的控件,然后我们就要对窗口A通过SetWindowRgn进行裁剪,通过镂空出控件的位置从而达到显示出控件。最后划分窗口B的大小,让窗口A显示出异形的模样,如图所示,红色区域也就是窗口B的大小

2

首先创建一个MFC类,取名CLayeredWindow,基类CWnd,也就是上文提到的窗口A,CLayeredWindow.h代码如下////////////////////////////////////////////////////////////////////////分层窗口class CLayeredWindow : public CWnd{    DECLARE_DYNAMIC(CLayeredWindow)             //变量定义protected:    CWnd *                          m_pWndControl;                      //控件窗口         //函数定义public:    //构造函数    CLayeredWindow();    //析构函数    virtual ~CLayeredWindow();         //功能函数public:    //创建窗口    VOID CreateLayered(CWnd * pWndControl, CRect & rcWindow);    //设置区域    VOID InitLayeredArea(CDC * pDCImage, BYTE cbAlpha, CRect & rcUnLayered, CPoint & PointRound, bool bUnLayeredChild);    //设置区域    VOID InitLayeredArea(CImageEx & Image, BYTE cbAlpha, CRect & rcUnLayered, CPoint & PointRound, bool bUnLayeredChild);         //消息映射protected:    //关闭消息    VOID OnClose();    //焦点消息    VOID OnSetFocus(CWnd * pOldWnd);         //静态函数protected:    //枚举函数    static BOOL CALLBACK EnumChildProc(HWND hWndChild, LPARAM lParam);         DECLARE_MESSAGE_MAP()};//////////////////////////////////////////////////////////////////////CLayeredWindow.cpp代码如下//枚举结构struct tagEnumChildInfo{    CWnd *                          pWndControl;                        //控制窗口    CWnd *                          pWndLayered;                        //分层窗口    CRgn *                          pRegionResult;                      //结果区域};     IMPLEMENT_DYNAMIC(CLayeredWindow, CWnd)     CLayeredWindow::CLayeredWindow(){    //设置变量    m_pWndControl=NULL;}     CLayeredWindow::~CLayeredWindow(){}          BEGIN_MESSAGE_MAP(CLayeredWindow, CWnd)END_MESSAGE_MAP()               //创建窗口VOID CLayeredWindow::CreateLayered(CWnd * pWndControl, CRect & rcWindow){    //效验参数    ASSERT((pWndControl!=NULL)&&(pWndControl->m_hWnd!=NULL));    if ((pWndControl==NULL)||(pWndControl->m_hWnd==NULL)) return;         //设置变量    m_pWndControl=pWndControl;         //创建窗口    CreateEx(WS_EX_LAYERED,TEXT('STATIC'),TEXT(''),0,rcWindow,pWndControl,0L);         return;}     //设置区域VOID CLayeredWindow::InitLayeredArea(CDC * pDCImage, BYTE cbAlpha, CRect & rcUnLayered, CPoint & PointRound, bool bUnLayeredChild){    //效验参数    ASSERT((pDCImage!=NULL)&&(pDCImage->m_hDC!=NULL));    if ((pDCImage==NULL)||(pDCImage->m_hDC==NULL)) return;         //变量定义    BITMAP Bitmap;    ZeroMemory(&Bitmap,sizeof(Bitmap));         //获取图像    CBitmap * pBitmap=pDCImage->GetCurrentBitmap();    if (pBitmap!=NULL) pBitmap->GetBitmap(&Bitmap);         //获取大小    CSize SizeImage;    SizeImage.SetSize(Bitmap.bmWidth,Bitmap.bmHeight);         //效验大小    ASSERT((SizeImage.cx>0)&&(SizeImage.cy>0));    if ((SizeImage.cx==0)||(SizeImage.cy==0)) return;         //变量定义    BLENDFUNCTION BlendFunction;    ZeroMemory(&BlendFunction,sizeof(BlendFunction));         //设置参数    BlendFunction.BlendOp=0;    BlendFunction.BlendFlags=0;    BlendFunction.AlphaFormat=AC_SRC_ALPHA;    BlendFunction.SourceConstantAlpha=cbAlpha;         //设置分层    CPoint ImagePoint(0,0);    CClientDC ClientDC(this);    UpdateLayeredWindow(&ClientDC,NULL,&SizeImage,pDCImage,&ImagePoint,0L,&BlendFunction,ULW_ALPHA);         //创建区域    CRgn RegionResult;    RegionResult.CreateRectRgn(0,0,SizeImage.cx,SizeImage.cy);         //窗口排除    if (bUnLayeredChild==true)    {        //变量定义        tagEnumChildInfo EnumChildInfo;        ZeroMemory(&EnumChildInfo,sizeof(EnumChildInfo));             //设置变量        EnumChildInfo.pWndLayered=this;        EnumChildInfo.pWndControl=m_pWndControl;        EnumChildInfo.pRegionResult=&RegionResult;             //枚举窗口        ASSERT(m_pWndControl->GetSafeHwnd()!=NULL);        EnumChildWindows(m_pWndControl->m_hWnd,EnumChildProc,(LPARAM)&EnumChildInfo);    }         //区域排除    if (rcUnLayered.IsRectEmpty()==FALSE)    {        //创建区域        CRgn RegionUnLayered;        RegionUnLayered.CreateRoundRectRgn(rcUnLayered.left,rcUnLayered.top,rcUnLayered.right+1,rcUnLayered.bottom+1,PointRound.x,PointRound.y);             //合并区域        RegionResult.CombineRgn(&RegionResult,&RegionUnLayered,RGN_DIFF);    }         //设置区域    SetWindowRgn(RegionResult,TRUE);         return;}     //设置区域VOID CLayeredWindow::InitLayeredArea(CImageEx & Image, BYTE cbAlpha, CRect & rcUnLayered, CPoint & PointRound, bool bUnLayeredChild){    //创建缓冲    CImage ImageBuffer;    ImageBuffer.Create(Image.GetWidth(),Image.GetHeight(),32);         //绘画界面    CImageDC ImageDC(ImageBuffer);    CDC * pBufferDC=CDC::FromHandle(ImageDC);         //绘画界面    ASSERT(pBufferDC!=NULL);    if (pBufferDC!=NULL) Image.DrawImage(pBufferDC,0,0);         //创建分层    InitLayeredArea(pBufferDC,cbAlpha,rcUnLayered,PointRound,bUnLayeredChild);         return;}     //关闭消息VOID CLayeredWindow::OnClose(){    //投递消息    if (m_pWndControl!=NULL)    {        m_pWndControl->PostMessage(WM_CLOSE);    }         return;}     //焦点消息VOID CLayeredWindow::OnSetFocus(CWnd * pOldWnd){    //设置焦点    if (m_pWndControl!=NULL)    {        m_pWndControl->SetFocus();    }}     //枚举函数BOOL CALLBACK CLayeredWindow::EnumChildProc(HWND hWndChild, LPARAM lParam){    //获取位置    CRect rcWindow;    ::GetWindowRect(hWndChild,&rcWindow);         //创建区域    if ((rcWindow.Width()>0)&&(rcWindow.Height()>0))    {        //变量定义        ASSERT(lParam!=0L);        tagEnumChildInfo * pEnumChildInfo=(tagEnumChildInfo *)lParam;             //窗口判断        HWND hWndParent=::GetParent(hWndChild);        if (hWndParent!=pEnumChildInfo->pWndControl->GetSafeHwnd())        {            return TRUE;        }             //转换位置        ASSERT(pEnumChildInfo->pWndControl!=NULL);        pEnumChildInfo->pWndControl->ScreenToClient(&rcWindow);             //创建区域        CRgn RgnChild;        RgnChild.CreateRectRgn(rcWindow.left,rcWindow.top,rcWindow.right,rcWindow.bottom);             //合并区域        ASSERT(pEnumChildInfo->pRegionResult!=NULL);        pEnumChildInfo->pRegionResult->CombineRgn(pEnumChildInfo->pRegionResult,&RgnChild,RGN_DIFF);    }         return TRUE;}

3

代码中我们实现了两个最主要的关键函数,创建分层和初始化分层在CreateLayered中,我们创建了一个静态窗口,而且拥有WS_EX_LAYERED属性,这个很重要,这是实现透明的关键;其次InitLayeredArea中,我们根据加载的图片,通过UpdateLayeredWindow实现窗口的异形,可能很多朋友不理解,下面怎么还枚举窗口干啥啊,有什么作用呢,上文中我们说过这个窗口A遮挡了主窗口的控件,那么我们自然就要枚举主窗口的所有控件,从而统计出这些控件的位置,当然光镂空出控件的位置还不够,因为我们需要在主窗口上绘图啊,所以这里我们才会合并区域,如图所示,黑色区域代表异形窗口A,红色区域为主窗口B,绿色为主窗口上面的控件,那么我们通过CombineRgn的RGN_DIFF重新计算区域的大小,这样一来就把异形窗口A镂空出了红色区域显示的部分。

4

接下来我们在做窗口B,逻辑窗口,创建mfc类,取名CLayeredDialog,基类选择CDialog其实这个类实现起来很简单,无非就是调用窗口A的函数而已,但是有一点不要忘记,别忘记裁剪窗口B,否则是实现不了异形的类声明如下://////////////////////////////////////////////////////////////////////class CLayeredDialog : public CDialog{    DECLARE_DYNAMIC(CLayeredDialog)       //变量定义protected:    CImageEx                    m_ImageBack;            //背景图片    CLayeredWindow              m_LayeredWindow;        //分层窗口       //函数定义public:    //构造函数    CLayeredDialog(UINT uTemplate, CWnd* pParent = NULL);    //析构函数    virtual ~CLayeredDialog();   protected:    //控件绑定    virtual void DoDataExchange(CDataExchange* pDX);    //绘画消息    virtual VOID OnDrawClientArea(CDC * pDC, INT nWidth, INT nHeight){}       //基本函数public:    //初始化    void SetLayeredDialog(HINSTANCE hInstance,LPCTSTR lpResource,CSize szSizeLT,CSize szSizeRB);    //绘画背景    BOOL OnEraseBkgnd(CDC * pDC);    //位置改变    VOID OnWindowPosChanged(WINDOWPOS * lpWndPos);    //显示消息    VOID OnShowWindow(BOOL bShow, UINT nStatus);    //去除边框    void RemoveBorder();       DECLARE_MESSAGE_MAP()};//////////////////////////////////////////////////////////////////////BEGIN_MESSAGE_MAP(CLayeredDialog, CDialog)    ON_WM_ERASEBKGND()    ON_WM_SHOWWINDOW()    ON_WM_WINDOWPOSCHANGED()END_MESSAGE_MAP()   // CLayeredDialog 对话框   IMPLEMENT_DYNAMIC(CLayeredDialog, CDialog)//////////////////////////////////////////////////////////////////////////   CLayeredDialog::CLayeredDialog(UINT uTemplate, CWnd* pParent /*=NULL*/): CDialog(uTemplate, pParent){}   CLayeredDialog::~CLayeredDialog(){}   void CLayeredDialog::DoDataExchange(CDataExchange* pDX){    CDialog::DoDataExchange(pDX);}   void CLayeredDialog::SetLayeredDialog( HINSTANCE hInstance,LPCTSTR lpResource,CSize szSizeLT,CSize szSizeRB ){    //设置窗口    RemoveBorder();       //加载资源    m_ImageBack.LoadImage(hInstance,lpResource);       //设置大小    CSize SizeWindow(m_ImageBack.GetWidth(),m_ImageBack.GetHeight());    SetWindowPos(NULL,0,0,SizeWindow.cx,SizeWindow.cy,SWP_NOZORDER|SWP_NOMOVE|SWP_NOREDRAW);       //获取窗口    CRect rcWindow;    GetWindowRect(&rcWindow);       //计算位置    CRect rcUnLayered;    rcUnLayered.top=szSizeLT.cy;    rcUnLayered.left=szSizeLT.cx;    rcUnLayered.right=rcWindow.Width()-szSizeRB.cx;    rcUnLayered.bottom=rcWindow.Height()-szSizeRB.cy;       //设置区域    CRgn RgnWindow;    RgnWindow.CreateRoundRectRgn(rcUnLayered.left,rcUnLayered.top,rcUnLayered.right+1,rcUnLayered.bottom+1,0,0);       //设置区域    SetWindowRgn(RgnWindow,FALSE);       //分层窗口    m_LayeredWindow.CreateLayered(this,rcWindow);    m_LayeredWindow.InitLayeredArea(m_ImageBack,200,rcUnLayered,CPoint(0,0),false);}   void CLayeredDialog::RemoveBorder(){    DWORD dwStyle = GetStyle();    DWORD dwNewStyle = WS_OVERLAPPED | WS_VISIBLE| WS_SYSMENU |WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_CLIPCHILDREN|WS_CLIPSIBLINGS;    dwNewStyle&=dwStyle;    SetWindowLong(m_hWnd,GWL_STYLE,dwNewStyle);    DWORD dwExStyle = GetExStyle();    DWORD dwNewExStyle = WS_EX_LEFT |WS_EX_LTRREADING |WS_EX_RIGHTSCROLLBAR;    dwNewExStyle&=dwExStyle;    SetWindowLong(m_hWnd,GWL_EXSTYLE,dwNewExStyle);}   //绘画背景BOOL CLayeredDialog::OnEraseBkgnd(CDC * pDC){    //获取位置    CRect rcClient;    GetClientRect(&rcClient);       //建立缓冲    CImage ImageBuffer;    ImageBuffer.Create(rcClient.Width(),rcClient.Height(),32);       if ( ImageBuffer.IsNull() ) return TRUE;       //创建 DC    CImageDC BufferDC(ImageBuffer);    CDC * pBufferDC=CDC::FromHandle(BufferDC);       //设置缓冲    pBufferDC->SetBkMode(TRANSPARENT);       //绘画背景    m_ImageBack.DrawImage(pBufferDC,0,0);       OnDrawClientArea(pBufferDC,rcClient.Width(),rcClient.Height());       //绘画界面    pDC->BitBlt(0,0,rcClient.Width(),rcClient.Height(),pBufferDC,0,0,SRCCOPY);       return TRUE;}   //显示消息VOID CLayeredDialog::OnShowWindow(BOOL bShow, UINT nStatus){    __super::OnShowWindow(bShow, nStatus);       //显示分层    if (m_LayeredWindow.m_hWnd!=NULL)    {        m_LayeredWindow.ShowWindow((bShow==FALSE)?SW_HIDE:SW_SHOW);    }       return;}   //位置改变VOID CLayeredDialog::OnWindowPosChanged(WINDOWPOS * lpWndPos){    __super::OnWindowPosChanging(lpWndPos);       //移动分层    if ((m_LayeredWindow.m_hWnd!=NULL)&&(lpWndPos->cx>=0)&&(lpWndPos->cy>0))    {        m_LayeredWindow.SetWindowPos(NULL,lpWndPos->x,lpWndPos->y,lpWndPos->cx,lpWndPos->cy,SWP_NOZORDER);    }       return;}这里主要的函数就一个SetLayeredDialog,通过这里,我们裁剪窗口B,同时创建窗口A,这里的代码很简单,不过多讲解了,这个函数我在后面写了两个参数,通过这两个去裁剪窗口大小,上图所示,第一个代表黑色区域左上角到红色区域的左上角的位置;第二个参数同理。这里我因为做演示用,就实现了一个矩形区域,当然你可以通过CRgn实现一个特殊的多边形,这样实例demo中的开始按钮就可以显示出来了,有兴趣的朋友可以修改一下,就几行代码的事情。关于CImageEx类,是CImage的封装类,大家自行封装一下即可

调用方法
1

将主对话框的基类CDialog全部替换成CLayeredDialog,然后在OnInitDialog()中SetLayeredDialog(AfxGetInstanceHandle(),TEXT('VIEW_BACK'),CSize(120,200),CSize(162,145));

2

当然如果你想绘制图形,你可以重载OnDrawClientArea进行绘制VOID CLayeredDlg::OnDrawClientArea( CDC * pDC, INT nWidth, INT nHeight ){    pDC->TextOut(500,550,TEXT('其实这种窗口实现起来很简单'));}

注意事项

SetWindowRgn裁剪窗口大小,但是并不代表裁剪掉了原始窗口的尺寸大小,它只是设置了一个窗口的区域.只有被包含在这个区域内的地方才会被重绘,而不包含在区域内的其他区域系统将不会显示.

推荐信息