1. GDI+配置及初始化(VS2013)
VS2013上已经有了GDI+支持,不用单独下载安装包了。现在只需项目中引入gdiplus.lib和加入头文件即可。
为了在全工程使用GDI+,在stdafx.h中加入:
#include <GdiPlus.h>
#pragma comment(lib, "GdiPlus.lib")
using namespace Gdiplus;
使用GDI+还需要进行初始化:
BOOL CXXXApp::InitInstance()
{
...
ULONG_PTR m_gdiplusToken;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
...
}
int CXXXApp::ExitInstance()
{
GdiplusShutdown(m_gdiplusToken);
return CWinApp::ExitInstance();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
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;
TCHAR lpImageFile[MAX_PATH];
swprintf_s(lpImageFile, TEXT("F:\\img\\region.png"));
m_BkgImg = Image::FromFile(lpImageFile, TRUE);
if (m_BkgImg == NULL || (m_BkgImg->GetLastStatus() != Ok))
{
if (m_BkgImg != NULL)
delete m_BkgImg;
m_BkgImg = NULL;
}
......
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
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,
_In_opt_ POINT *pptDst,
_In_opt_ SIZE *psize,
_In_opt_ HDC hdcSrc,
_In_opt_ POINT *pptSrc,
_In_ COLORREF crKey,
_In_opt_ BLENDFUNCTION *pblend,
_In_ DWORD dwFlags
);
内存DC是很重要的一个概念,假如你要对屏幕进行比较多的GDI函数操作,如果每一步操作都直接对屏幕dc进行操作,那出现的大多数可能性都是屏幕的闪烁。一个很好的解决方法就是使用内存dc,将这些操作全部先在内存dc上操作,然后依次性在屏幕上进行操作。
void CXXXDlg::OnPaint()
{
CClientDC clientdc(this);
HDC hRgnDC;
HBITMAP hRgnBmp, hOldRgnBmp;
int RgnWidth = 500;
int RgnHeight = 400;
hRgnDC = ::CreateCompatibleDC(clientdc.GetSafeHdc());
hRgnBmp = ::CreateCompatibleBitmap(clientdc.GetSafeHdc(), RgnWidth, RgnHeight);
hOldRgnBmp = (HBITMAP)SelectObject(hRgnDC, hRgnBmp);
if (m_BkgImg != NULL){
Graphics graph(hRgnDC);
Point points[] = { Point(0, 0),
Point(RgnWidth, 0),
Point(0, RgnHeight)
};
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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
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