在上几篇中,我们说到了通过GdipCreateFromHDC函数将HDC(设备上下文句柄)装换成GDI+的Graphics对象,讲到了画笔和画刷的使用,演示了何利用GDI+Flat来画图片和画文字,讲到了双缓冲绘图和DC复制,并说了用GdiplusFlat来绘制按钮的方法1。
这一篇博文中,我们来聊聊如何用GdiplusFlat来绘制按钮的方法2。
还是和以前一样,我们必须自己声明GDI+Flat函数,自己定义GDI+Flat的数据结构。自己动手,丰衣足食。~~
在上一篇中我们已经知道了,自绘按钮有两种形式,这两种形式分别是:
1。我们不创建按钮,我们直接在窗口的设备上下文场景的一部分上绘制按钮,也就是说不存在一个子窗口,按钮完全是我们自己画上去的。
2。将一个标准的win32按钮子类化(子类化介绍),在按钮的WM_PAINT、WM_MOUSEMOVE、WM_MOUSELEAVE、WM_LBUTTONDOWN、WM_LBUTTONUP消息里面控制按钮的绘制,也就是说,我们直接基于一个Win32按钮,我们是改变了这个win32按钮的实现逻辑,绘制成我们需要的样式,按钮依旧作为子窗口存在。
上一篇讲了方法1,这一篇文章讲一讲方法2
上一篇地址:http://blog.csdn.net/zuishikonghuan/article/details/47394747
效果和上一篇中的一样,但方法略有不同,这种方法不需要判断鼠标指针位置,但需要对按钮子类化。
关于子类化的:http://blog.csdn.net/zuishikonghuan/article/details/47169605
效果图:(和上一篇一样)(GIF本身的限制,动画有颜色失真)
完整源码
#include "stdafx.h" #include <gdiplus.h> #include <gdiplusflat.h> #pragma comment(lib,"gdiplus.lib")//very important #include <windows.h> #include <windowsx.h> #pragma comment(lib,"user32.lib") #pragma comment(lib,"gdi32.lib") #pragma comment(lib,"Msimg32.lib") //GDI+Flat #define ARGB(a,r,g,b) ((int)(((BYTE)a)<<24)|(((BYTE)r)<<16)|(((BYTE)g)<<8)|((BYTE)b)) typedef struct _GdiplusStartupInput{ unsigned int GdiplusVersion; unsigned int DebugEventCallback; BOOL SuppressBackgroundThread; BOOL SuppressExternalCodecs; }GdiplusStartupInput; extern "C" int WINAPI GdiplusStartup(int* token, GdiplusStartupInput *input, int *output); extern "C" void WINAPI GdiplusShutdown(int token); extern "C" int WINAPI GdipCreateFromHDC(HDC hdc, int* graphics); extern "C" int WINAPI GdipDeleteGraphics(int graphics); //画笔 extern "C" int WINAPI GdipCreatePen1(unsigned int argb_color, float width, int unit, int** pen); extern "C" int WINAPI GdipDeletePen(int* pen); //画矩形 画直线 extern "C" int WINAPI GdipDrawRectangle(int graphics, int* pen, float x, float y, float width, float height); extern "C" int WINAPI GdipDrawLine(int graphics, int* pen, float x1, float y1, float x2, float y2); //画刷 typedef struct _PointF{ float x; float y; }PointF; extern "C" int WINAPI GdipCreateSolidFill(unsigned int argb_color, int** brush); extern "C" int WINAPI GdipCreateLineBrush(PointF* point1, PointF* point2, unsigned int argb_color1, unsigned int argb_color2, int wrapMode, int** lineGradient); extern "C" int WINAPI GdipDeleteBrush(int* brush); //画填充矩形 extern "C" int WINAPI GdipFillRectangle(int graphics, int* brush, float x, float y, float width, float height); //画图片 extern "C" int WINAPI GdipLoadImageFromFile(WCHAR* filename, int** image); extern "C" int WINAPI GdipLoadImageFromStream(LPSTREAM stream, int** image); extern "C" int WINAPI GdipGetImageDimension(int* image, float* width, float* height); extern "C" int WINAPI GdipDrawImageRect(int graphics, int* image, float x, float y, float width, float height); extern "C" int WINAPI GdipDisposeImage(int* image); //画文字 typedef struct _RectF{ float x; float y; float Width; float Height; }RectF; extern "C" int WINAPI GdipSetTextRenderingHint(int graphics, int mode); extern "C" int WINAPI GdipSetSmoothingMode(int graphics, int smoothingMode); extern "C" int WINAPI GdipCreateFontFamilyFromName(WCHAR* name, int* fontCollection, int** fontFamily); extern "C" int WINAPI GdipCreateStringFormat(int formatAttributes, short language, int** format); extern "C" int WINAPI GdipSetStringFormatAlign(int* format, int align); extern "C" int WINAPI GdipSetStringFormatLineAlign(int* format, int align); extern "C" int WINAPI GdipCreateFont(int* fontFamily, float emSize, int style, int unit, int** font); extern "C" int WINAPI GdipDrawString(int graphics, WCHAR* string, int length, int* font, RectF* layoutRect, int* stringFormat, int* brush); extern "C" int WINAPI GdipDeleteFont(int* font); extern "C" int WINAPI GdipDeleteStringFormat(int* format); extern "C" int WINAPI GdipDeleteFontFamily(int* fontFamily); int token; int* pen; int* brush; int* linebrush; int* image; HDC mdc; void WINAPI MyDrawText(HDC hdc, LPCTSTR str, LONG left, LONG top, LONG width, LONG height, BOOL iscenter, COLORREF color = 0){ SetBkMode(hdc, TRANSPARENT); RECT r = { left, top, left + width, top + height }; HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); HFONT hOldFont = (HFONT)SelectObject(hdc, hFont); SetTextColor(hdc, color); DrawText(hdc, str, -1, &r, iscenter ? DT_CENTER | DT_VCENTER | DT_SINGLELINE : 0); SelectObject(hdc, hOldFont); DeleteObject(hFont); }//参数:设备上下文句柄,要绘制的字符串,x位置,y位置,宽度,高度,是否绘制在矩形中央,颜色 //************************************************************* LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); WNDCLASS wc; const TCHAR* AppName = TEXT("MyWindowClass1"); HWND hwnd1; BOOL Track; WNDPROC lastproc; HWND button; int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { //GDI+开启 GdiplusStartupInput StartupInput = { 0 }; StartupInput.GdiplusVersion = 1; if (GdiplusStartup(&token, &StartupInput, NULL))MessageBox(0, TEXT("GdiPlus开启失败"), TEXT("错误"), MB_ICONERROR); //这里是在构建窗口类结构 wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc;//窗口回调函数指针 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance;//实例句柄 wc.hIcon = LoadIcon(hInstance, TEXT("ICON_1")); wc.hCursor = LoadCursor(NULL, IDC_ARROW);//默认指针 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = AppName;//窗口类名 //注册窗口类 if (!RegisterClass(&wc)) { MessageBox(NULL, TEXT("注册窗口类失败!"), TEXT("错误"), MB_ICONERROR); return 0; } //创建窗口 int style = WS_OVERLAPPEDWINDOW; hwnd1 = CreateWindowEx(NULL, AppName, TEXT("窗口标题"), style, 50, 50, 500, 500, 0, LoadMenu(hInstance, TEXT("MENU1")), hInstance, 0); if (hwnd1 == NULL) { MessageBox(NULL, TEXT("创建窗口失败!"), TEXT("错误"), MB_ICONERROR); return 0; } //无边框窗口 SetWindowLong(hwnd1, GWL_STYLE, WS_OVERLAPPED | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); //显示、更新窗口 ShowWindow(hwnd1, nCmdShow); UpdateWindow(hwnd1); //消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } //GDI+关闭 GdiplusShutdown(token);//可以把这个写在消息循环后面,程序退出就销毁,或者在不需要GDI+时调用,比如GDI+窗口的WM_DESTROY消息里调用 return msg.wParam; } //参数:设备上下文句柄;按钮背景ARGB颜色 void WINAPI drawbutton(HDC hdc, unsigned int argb){ int graphics; GdipCreateFromHDC(mdc, &graphics);//创建Graphics对象 GdipCreateSolidFill(0xFFFFFFFF, &brush); GdipFillRectangle(graphics, brush, 0, 0, 500, 500); GdipDeleteBrush(brush);//将内存DC背景涂白 GdipCreateSolidFill(argb, &brush);//创建单色画刷 GdipFillRectangle(graphics, brush, 0, 0, 120, 25);//用单色画刷填充矩形 GdipDeleteBrush(brush);//销毁画刷 GdipCreatePen1(ARGB(255, 185, 206, 230), 1, 2, &pen);//创建画笔 GdipDrawRectangle(graphics, pen, 0, 0, 119, 24);//画矩形 GdipDeletePen(pen);//销毁画笔 MyDrawText(mdc, TEXT("按钮"), 0, 0, 120, 26, true); StretchBlt(hdc, 0, 0, 120, 25, mdc, 0, 0, 120, 25, SRCCOPY);//DC复制 GdipDeleteGraphics(graphics);//销毁Graphics对象 } LRESULT CALLBACK ButtonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { int xPos, yPos; switch (uMsg){ case WM_PAINT: HDC hdc; PAINTSTRUCT ps; hdc = BeginPaint(hwnd, &ps); drawbutton(hdc, ARGB(255, 240, 240, 240)); EndPaint(hwnd, &ps); return 0;//告诉系统,WM_PAINT消息我已经处理了,你那儿凉快哪儿玩去吧。 break; case WM_MOUSEMOVE://鼠标移动 xPos = GET_X_LPARAM(lParam);//鼠标位置X坐标 yPos = GET_Y_LPARAM(lParam);//鼠标位置Y坐标//不要用LOWORD和HIWORD获取坐标,因为坐标有可能是负的 if (Track == false){ TRACKMOUSEEVENT a; a.cbSize = sizeof(TRACKMOUSEEVENT); a.dwFlags = TME_LEAVE; a.hwndTrack = hwnd; if (TrackMouseEvent(&a) != 0){ Track == true; } } hdc = GetDC(hwnd); drawbutton(hdc, ARGB(255, 64, 198, 255)); return 0; break; case WM_MOUSELEAVE: Track = false; if (1){ hdc = GetDC(hwnd); drawbutton(hdc, ARGB(255, 240, 240, 240)); } return 0; break; case WM_LBUTTONDOWN: xPos = GET_X_LPARAM(lParam); yPos = GET_Y_LPARAM(lParam); hdc = GetDC(hwnd); drawbutton(hdc, ARGB(50, 255, 0, 0)); SetCapture(hwnd);//捕获鼠标 return 0; break; case WM_LBUTTONUP: hdc = GetDC(hwnd); drawbutton(hdc, ARGB(255, 240, 240, 240)); xPos = GET_X_LPARAM(lParam); yPos = GET_Y_LPARAM(lParam); //在这里添加按钮单击的事件 MessageBox(NULL, TEXT("你按下了按钮"), TEXT("自绘按钮"), MB_ICONINFORMATION); return 0; break; } return CallWindowProc(lastproc, hwnd, uMsg, wParam, lParam);//让原来的窗口回调函数处理其他消息 } LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg){ case WM_CREATE: mdc = CreateCompatibleDC(0); BITMAPINFO bitmapinfo; RtlZeroMemory(&bitmapinfo, sizeof(BITMAPINFO)); bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bitmapinfo.bmiHeader.biWidth = 120; bitmapinfo.bmiHeader.biHeight = 25; bitmapinfo.bmiHeader.biPlanes = 1; bitmapinfo.bmiHeader.biBitCount = 32; HBITMAP b, old; b = CreateDIBSection(0, &bitmapinfo, 0, 0, 0, 0); old = (HBITMAP)SelectObject(mdc, b); if (old != NULL){ DeleteObject(old); } DeleteObject(b); button = CreateWindow(TEXT("Button"), NULL, WS_CHILD | WS_VISIBLE|BS_PUSHBOX|BS_OWNERDRAW, 20, 20, 120, 25, hwnd, (HMENU)5, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL); lastproc = (WNDPROC)SetWindowLongPtr(button, GWL_WNDPROC, (LONG)&ButtonProc);//创建win32按钮控件并子类化 return 0; break; case WM_DESTROY://窗口已经销毁 DeleteDC(mdc); PostQuitMessage(0);//退出消息循环,结束应用程序 return 0; break; case WM_LBUTTONDOWN: //让无边框窗口能够拖动(在窗口客户区拖动) PostMessage(hwnd, WM_SYSCOMMAND, 61458, 0); break; default: break; } return DefWindowProc(hwnd, uMsg, wParam, lParam);//其他消息交给系统处理 }