MFC中异形分层窗口设计

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

方法/步骤

  1. 1

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

    MFC中异形分层窗口设计
  2. 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. 3

    代码中我们实现了两个最主要的关键函数,创建分层和初始化分层

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

    MFC中异形分层窗口设计
  4. 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的封装类,大家自行封装一下即可

    END

调用方法

  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("其实这种窗口实现起来很简单"));

    }

    END

注意事项

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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值