MFC界面编程1:GDI+实现不规则窗体

先来看一下实现的效果,下图中那个娃娃就是我们要实现的不规则窗体啦~
不规则窗体

1. GDI+配置及初始化(VS2013)

VS2013上已经有了GDI+支持,不用单独下载安装包了。现在只需项目中引入gdiplus.lib和加入头文件即可。
为了在全工程使用GDI+,在stdafx.h中加入:

#include <GdiPlus.h>
#pragma comment(lib, "GdiPlus.lib")
using namespace Gdiplus;

使用GDI+还需要进行初始化:

//在应用初始化时,启动GDI+  
BOOL CXXXApp::InitInstance()  
{  
    ...  
   //Initialize GDIplus
    ULONG_PTR m_gdiplusToken;
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;   
    GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);  
    ...  
}  
//在程序退出时,关闭gdi+  
int CXXXApp::ExitInstance()  
{  
    //close gdiplus environment  
    GdiplusShutdown(m_gdiplusToken);  
    return CWinApp::ExitInstance();  
}  

2. 动态载入png图片

png图片是实现不规则窗体的关键,因为它本身就是带透明效果的,把这种格式的图片作为窗体本身就是不规则的。首先我们需要载入这张图片,在这里我没有将其作为资源添加到项目中,这样对于更改图片不是很方便,所以用的是动态载入,将图片直接载入到内存中。
(1) 在CXXXDlg.h中添加成员变量:

Gdiplus::Image *m_BkgImg;//不规则背景图片
BLENDFUNCTION m_Blend; //指定源和目标位图的透明混合参数

(2) 在CXXXDlg.cpp中添加如下代码:

BOOL CXXXDlg::OnInitDialog()
{
    ......
    m_Blend.BlendOp = AC_SRC_OVER;
    m_Blend.BlendFlags = 0;
    m_Blend.AlphaFormat = AC_SRC_ALPHA;
    m_Blend.SourceConstantAlpha = 200;//255:src img在dest img上不透明


    TCHAR lpImageFile[MAX_PATH];
    swprintf_s(lpImageFile, TEXT("F:\\img\\region.png"));//图片存放位置

    // load from file  
    m_BkgImg = Image::FromFile(lpImageFile, TRUE);

    if (m_BkgImg == NULL || (m_BkgImg->GetLastStatus() != Ok))
    {
        if (m_BkgImg != NULL)
            delete m_BkgImg;
        m_BkgImg = NULL;
    }
    ......
}

3. 绘制不规则窗体

首先使用CreatCompatibleDC()创建了一个和当前屏幕的DC兼容的内存DC(DC就是设备上下文的意思,设备上下文就是当前的这个窗体的一些属性,譬如说他使用的画刷,画笔等等),在绘制位图的时候,你必须要在内存中建立这样的一个和当前设备的环境兼容的DC,这样你才能把位图加载到这块内存里,然后再将位图从内存复制到屏幕DC上,位图才能显示出来。
用双缓冲的话还要再定义一个位图对象,然后用CreateCompatibleBitmap建立一个与屏幕显示兼容的位图,再用SelectObject将位图选入到内存显示设备中。
在绘制的时候有一个重要的函数叫做UpdateLayeredWindow,可以根据图像的透明度进行显示,函数原型是:
(官网定义)https://msdn.microsoft.com/en-us/library/windows/desktop/ms633556(v=vs.85).aspx

BOOL WINAPI UpdateLayeredWindow(
  _In_     HWND          hwnd,//窗口句柄
  _In_opt_ HDC           hdcDst,//屏幕上下文DC
  _In_opt_ POINT         *pptDst,//窗口相对于屏幕的位置的POINT结构的指针
  _In_opt_ SIZE          *psize,//显示窗口大小
  _In_opt_ HDC           hdcSrc,//窗口上下文DC
  _In_opt_ POINT         *pptSrc,//窗口绘图表面在设备上下文位置的POINT结构的指针
  _In_     COLORREF      crKey,
  _In_opt_ BLENDFUNCTION *pblend,//透明混合参数
  _In_     DWORD         dwFlags
);

内存DC是很重要的一个概念,假如你要对屏幕进行比较多的GDI函数操作,如果每一步操作都直接对屏幕dc进行操作,那出现的大多数可能性都是屏幕的闪烁。一个很好的解决方法就是使用内存dc,将这些操作全部先在内存dc上操作,然后依次性在屏幕上进行操作。

void CXXXDlg::OnPaint()
{
        CClientDC clientdc(this);
    HDC hRgnDC; //Region窗口
    HBITMAP hRgnBmp, hOldRgnBmp;
    int RgnWidth = 500;
    int RgnHeight = 400;

    // 创建一个内存DC 与当前屏幕DC兼容
    hRgnDC = ::CreateCompatibleDC(clientdc.GetSafeHdc());
    // 创建一个位图 与当前屏幕DC兼容
    hRgnBmp = ::CreateCompatibleBitmap(clientdc.GetSafeHdc(), RgnWidth, RgnHeight);
    //将位图选入到内存DC中,只有选入了位图的内存DC才有地方绘图,画到指定的位图上
    hOldRgnBmp = (HBITMAP)SelectObject(hRgnDC, hRgnBmp);
    if (m_BkgImg != NULL){
        Graphics graph(hRgnDC);
        Point points[] = { Point(0, 0),
            Point(RgnWidth, 0), //width
            Point(0, RgnHeight)//height
        };

        RECT rct;
        GetWindowRect(&rct);
        POINT ptWinPos = { rct.left, rct.top };

        graph.DrawImage(m_BkgImg, points, 3);

        SIZE sizeWindow = { RgnWidth, RgnHeight };
        POINT ptSrc = { 0, 0 };
        DWORD dwExStyle = GetWindowLong(m_hWnd, GWL_EXSTYLE);
        if ((dwExStyle & 0x80000) != 0x80000)
            SetWindowLong(m_hWnd, GWL_EXSTYLE, dwExStyle ^ 0x80000);

        BOOL bRet = FALSE;
        bRet = ::UpdateLayeredWindow(m_hWnd, hRgnDC, &ptWinPos,
            &sizeWindow, hRgnDC, &ptSrc, 0, &m_Blend, 2);

        ::SelectObject(hRgnDC, hOldRgnBmp);
        ::DeleteObject(hRgnBmp);
        ::DeleteDC(hRgnDC);

    }
}

4. Done and done!

再来一张图,换图就是如此方便~~
这里写图片描述

参考:
【VC++玩转炫酷悬浮窗3—GDI+完美实现不规则窗体】http://blog.csdn.net/lincyang/article/details/39078295
【CreateCompatibleDC】http://www.cnblogs.com/CBDoctor/archive/2012/12/09/2810447.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值