前言:在实际开发中,虽然有已经成形的界面库DUILIB,但DUILIB无法加载异形窗体,对PNG图片的支持不到位,最终我下决心,自己开发一套界面库,利用GDI+完成2D和3D功能,在这里,我将要记录下,我所做的界面开发的过程,并将它们整理成博客分享给大家,一步步来学习吧。
内容概要:这篇主要实现的功能是加载一个背景界面,向大家介绍下,如何用GDI+加载背景界面的问题,然后就是如何在指定位置托动窗体。
1、因为是GDI+绘图,所以对于GDI+的初始化是必须的,在stdafx.h里加入下面代码:
#include <gdiplus.h> #pragma comment(lib,"gdiplus.lib"); using namespace Gdiplus;2、首先看一下WINMAIN函数的写法:
ULONG_PTR gdiplusToken = 0; int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd){ Gdiplus::GdiplusStartupInput gdiplusStartupInput; Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); Register(WindowProc,hInstance,L"transparent"); HWND hwnd=Create(L"transparent",L"TEST",hInstance); Display(hwnd); Message(); Gdiplus::GdiplusShutdown(gdiplusToken); return 0; }讲解:
BOOL Register(WNDPROC fWndProc,HINSTANCE hInstance,LPCTSTR szClassName){ WNDCLASSEX wce={0}; wce.cbSize=sizeof(wce); wce.style=CS_HREDRAW|CS_VREDRAW; wce.lpfnWndProc=fWndProc; wce.cbClsExtra=0; wce.cbWndExtra=0; wce.hInstance=hInstance; wce.hIcon=NULL; wce.hCursor=LoadCursor(NULL,IDC_ARROW); wce.hbrBackground=(HBRUSH)(6); wce.lpszMenuName=NULL; wce.lpszClassName=szClassName; wce.hIconSm=NULL; ATOM nAtom=RegisterClassEx(&wce); if(nAtom==0) return false; return true; }讲解:注册窗口类也没什么好讲的,就是注册WNDCLASSEX结构体,首先对其各项进行赋值,然后调用RegisterClassEx()对其注册,但这里注意的两个地方,fWndPorc是传进来的消息处理函数的指针,我们就是在这个函数里截获窗体有关的各种消息,然后对其进行处理。szClassName是要注册窗体的类名;不同的窗口类,类名不能相同;
4、WndProc消息处理函数的写法;
LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam){ switch(uMsg){ case WM_DESTROY: PostQuitMessage(100); break; case WM_CREATE: { LONG styleValue = ::GetWindowLong(hwnd, GWL_STYLE); styleValue &= ~WS_CAPTION; styleValue &= ~WS_MAXIMIZEBOX; styleValue &= ~WS_MINIMIZEBOX; styleValue &= ~WS_THICKFRAME; styleValue &= ~WS_BORDER; styleValue &= ~WS_CAPTION; ::SetWindowLong(hwnd, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); } break; case WM_PAINT: break; case WM_LBUTTONDOWN: break; } return DefWindowProc(hwnd,uMsg,wParam,lParam); }5、创建窗体(Create函数)写法
HWND Create(LPCTSTR lpClassName,LPCTSTR lpWindowName,HINSTANCE hInstance){ HWND m_hWnd = ::CreateWindowEx(WS_EX_LAYERED, L"transparent", _T(""),WS_POPUPWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, (HINSTANCE)::GetModuleHandle(NULL), 0); if(m_hWnd == NULL || !::IsWindow(m_hWnd)) return NULL; return m_hWnd; }6、显示窗体(Display函数)
void Display(HWND hwnd){ ShowWindow(hwnd,SW_SHOWNORMAL); UpdateWindow(hwnd); }7、消息循环(Message()函数)
void Message(){ MSG msg={0}; while(GetMessage(&msg,NULL,0,0)){ TranslateMessage(&msg); DispatchMessage(&msg); } }
1、添加消息响应,在WM_PAINT消息中,添加消息响应SetBackground(hwnd,L"C:\\bg.png");
case WM_PAINT: SetBackground(hwnd,L"C:\\bg.png"); break;2、SetBackground函数实现
void SetBackground(HWND m_hWnd,const TCHAR* pBackImgFullPath) { //加载图片 Gdiplus::Image* pImage = Gdiplus::Image::FromFile(pBackImgFullPath); if (pImage==NULL) { assert(false && _T("背景图片打开失败!")); } RECT windowRect; GetWindowRect(m_hWnd,&windowRect); SIZE sizeWindow; if (windowRect.left==windowRect.right) { sizeWindow.cx=pImage->GetWidth(); sizeWindow.cy=pImage->GetHeight(); }else { sizeWindow.cx=windowRect.right-windowRect.left; sizeWindow.cy=windowRect.bottom-windowRect.top; } HDC hDC = ::GetDC(m_hWnd); HDC hdcMemory = CreateCompatibleDC(hDC); RECT rcWindow; GetWindowRect(m_hWnd,&rcWindow); BITMAPINFOHEADER stBmpInfoHeader = { 0 }; int nBytesPerLine = ((sizeWindow.cx * 32 + 31) & (~31)) >> 3; stBmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER); stBmpInfoHeader.biWidth = sizeWindow.cx; stBmpInfoHeader.biHeight = sizeWindow.cy; stBmpInfoHeader.biPlanes = 1; stBmpInfoHeader.biBitCount = 32; stBmpInfoHeader.biCompression = BI_RGB; stBmpInfoHeader.biClrUsed = 0; stBmpInfoHeader.biSizeImage = nBytesPerLine * sizeWindow.cy; PVOID pvBits = NULL; HBITMAP hbmpMem = ::CreateDIBSection(NULL, (PBITMAPINFO)&stBmpInfoHeader, DIB_RGB_COLORS, &pvBits, NULL, 0); assert(hbmpMem != NULL); HGDIOBJ hbmpOld = ::SelectObject( hdcMemory, hbmpMem); POINT ptWinPos = { rcWindow.left, rcWindow.top }; Gdiplus::Graphics graph(hdcMemory); graph.SetSmoothingMode(Gdiplus::SmoothingModeNone); graph.DrawImage(pImage, 0, 0, sizeWindow.cx, sizeWindow.cy); graph.FillRectangle(&SolidBrush(Color::Gray),0,0,100,50); //用GDI+在画布上画图 HMODULE hFuncInst = LoadLibrary(_T("User32.DLL")); typedef BOOL (WINAPI *MYFUNC)(HWND, HDC, POINT*, SIZE*, HDC, POINT*, COLORREF, BLENDFUNCTION*, DWORD); MYFUNC UpdateLayeredWindow; UpdateLayeredWindow = (MYFUNC)::GetProcAddress(hFuncInst, "UpdateLayeredWindow"); POINT ptSrc = { 0, 0}; BLENDFUNCTION blendFunc; blendFunc.BlendOp = 0; blendFunc.BlendFlags = 0; blendFunc.AlphaFormat = 1; blendFunc.SourceConstantAlpha = 255;//AC_SRC_ALPHA //不会发送 WM_SIZE和WM_MOVE消息 if(!UpdateLayeredWindow(m_hWnd, hDC, &ptWinPos, &sizeWindow, hdcMemory, &ptSrc, 0, &blendFunc, ULW_ALPHA)) { assert(L"UpdateLayeredWindow 调用失败"); TCHAR tmp[255] = {_T('\0')}; } delete pImage; graph.ReleaseHDC(hdcMemory); ::SelectObject( hdcMemory, hbmpOld); ::DeleteObject(hFuncInst); ::DeleteObject(hbmpOld); ::DeleteObject(hbmpMem); ::DeleteDC(hdcMemory); ::DeleteDC(hDC); }讲解:两个关键部分注意一下:
BITMAPINFOHEADER stBmpInfoHeader = { 0 }; int nBytesPerLine = ((sizeWindow.cx * 32 + 31) & (~31)) >> 3; stBmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER); stBmpInfoHeader.biWidth = sizeWindow.cx; stBmpInfoHeader.biHeight = sizeWindow.cy; stBmpInfoHeader.biPlanes = 1; stBmpInfoHeader.biBitCount = 32; stBmpInfoHeader.biCompression = BI_RGB; stBmpInfoHeader.biClrUsed = 0; stBmpInfoHeader.biSizeImage = nBytesPerLine * sizeWindow.cy; PVOID pvBits = NULL; HBITMAP hbmpMem = ::CreateDIBSection(NULL, (PBITMAPINFO)&stBmpInfoHeader, DIB_RGB_COLORS, &pvBits, NULL, 0);这是创建一个32位的位图,这部分代码必须有,否则会出现问题,我们的绘图就是在这个位图上画,然后再显示的桌面上;
HMODULE hFuncInst = LoadLibrary(_T("User32.DLL")); typedef BOOL (WINAPI *MYFUNC)(HWND, HDC, POINT*, SIZE*, HDC, POINT*, COLORREF, BLENDFUNCTION*, DWORD); MYFUNC UpdateLayeredWindow; UpdateLayeredWindow = (MYFUNC)::GetProcAddress(hFuncInst, "UpdateLayeredWindow"); POINT ptSrc = { 0, 0}; BLENDFUNCTION blendFunc; blendFunc.BlendOp = 0; blendFunc.BlendFlags = 0; blendFunc.AlphaFormat = 1; blendFunc.SourceConstantAlpha = 255;//AC_SRC_ALPHA //不会发送 WM_SIZE和WM_MOVE消息 if(!UpdateLayeredWindow(m_hWnd, hDC, &ptWinPos, &sizeWindow, hdcMemory, &ptSrc, 0, &blendFunc, ULW_ALPHA)) { assert(L"UpdateLayeredWindow 调用失败"); TCHAR tmp[255] = {_T('\0')}; }讲解:这是UpdateLayeredWindow的标准写法,UpdateLayeredWindow支持ALPHA通道,所以支持PNG图片的显示,但需要注意的是UpdateLayeredWindow不会发送WM_PAINT消息,所以我们要在WM_CREATE消息中,添加一行
PostMessageW(hwnd,WM_PAINT,NULL,NULL);最终的效果:
case WM_LBUTTONDOWN: SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0); break;至此,这篇的实现就全部讲完了,与往常一样,代码地址: http://download.csdn.net/detail/harvic880925/5757093